任何对泛型类型进行操作的优雅方式
本文关键字:方式 操作 泛型类型 任何 | 更新日期: 2023-09-27 18:02:19
我在一个小型教育项目中工作,我们必须实现一个n维矩阵。根据上下文的不同,这个矩阵可以使用我们自己的内置ComplexNumber
结构体,也可以使用System.Double
结构体,对于非常简单的例子,可以使用整型(主要是System.Int32
)。
由于应用程序的性质,我们不要求实现闪电般的快速性能。
因此,我的第一个想法是实现Matrix<T>
,其中T
将以某种方式需要限制为"数字"。
这样做的明显问题是,目前在语言中没有办法用定义的操作符约束泛型类型T
。我也没有看到一个简单的方法来限制T
为合理的类型。
我的问题是:
-
谁能给我指出一个优雅的方法来使用泛型进行数学运算,而不会对性能造成太大的损害,并且以某种方式使其与内置类型一起工作(如果可能的话)。
-
如果Eric读过这篇文章,那么这个特性(通过定义的操作符约束泛型类型)是否会在假设的c#设计会议的未来版本中出现,并且它是否已经接近成为语言?
我知道实现ComplexMatrix
类型并为每个矩阵"子类型"(双精度,积分等)创建包装器更容易和更好,并支付我们的复杂类型和矩阵元素碰巧是的任何类型之间转换的性能成本。这个问题更多的是出于好奇,人们会如何实现类似的场景。
如果Eric读到这篇文章,
如果你想让我注意到什么,试试我博客上的联系链接。或者把我的全名写在问题的正文里,这样我在寻找自己的时候就能找到自己。
这个特性(通过定义的操作符约束泛型类型)是否会在c#设计会议的假设未来版本中出现?它是否已经接近成为语言?
确实,这是一个经常被请求的特性。从c# 1.0开始,我们就收到了这样的请求。
这个特性需要CLR团队的支持,而不仅仅是语言的支持——这是我们想要集成到所有语言中的那种特性,这会增加成本。
CLR团队已经表达了对这样的特性的兴趣,但是他们也有很多可以做的竞争特性,而且实现这些特性的时间和精力都是有限的。
有许多方法可以实现这样的功能。例如,我们可以添加在接口中指定静态方法的能力:
interface IAddable<T>
{
static T operator+(T x, T y);
}
和
static T Sum<T>(IEnumerable<T> seq) where T : IAddable<T>
{
T sum = default(T);
foreach(T item in seq) sum = sum + item;
return sum;
}
这个想法是接口意味着"实现这个接口的类型必须具有给定的静态方法"。然后让int自动实现IAddable<int>
,以此类推。
如何在运行时生成泛型代码的世界中有效地做到是一个悬而未决的问题。
我赶紧补充一下,这只是一个想法的草图。有许多方法可以实现这类功能。"界面中的静态"的概念比数学应用更广泛,这对我们很有吸引力。如果我们要为这类功能付出巨大的代价,最好有一个真正通用的、强大的功能,而不是一个狭隘地专注于数学的功能。
另一方面,完美是好的敌人;也许把注意力集中在数学问题上会更好,而不是去寻找更昂贵的通解。
这是一个正在进行的辩论。它肯定在每个人的雷达屏幕上,但我不期望它很快。语言设计师们都在埋头研究异步CTP的反馈。
一如既往,Eric对假设的未公布的未来产品的假设的未来语言特性的思考只是为了娱乐目的。
这取决于你对什么是优雅的看法。如果你认为的优雅是能够在a
和b
是泛型类型的地方编写a+b
,那将是我对优雅的看法,那么这是不可能做到的。
遗憾的是,对于这种类型的代码,c#泛型无法达到c++模板的优雅。
实现这个目标的唯一方法是:让调用方为所需的操作符指定委托,并使用这些委托。
例如:
class Matrix<T>
{
Func<T, T, T> _add;
Func<T, T, T> _subtract;
// ...
public Matrix(Func<T, T, T> add, Func<T, T, T> subtract, ...)
{
_add = add;
_subtract = subtract;
// ...
}
}
var m = new Matrix<int>((a,b) => a+b, (a,b) => a-b, ...);
// Assuming that ComplexNumber has two static methods Add and Subtract
var m = new Matrix<ComplexNumber>(ComplexNumber.Add, ComplexNumber.Subtract, ..);
但是,我不知道这种方法的性能如何…
使用一个结构体封装您想要的算术行为,这是一个性能良好(但仍然很难看)的解决方案。
首先定义一个接口:
public interface IArithmetic<T>
{
T Add(T n1,T n2);
}
然后使用struct
:
public struct DoubleArithmetic:IArithmetic<double>
{
public double Add(double n1,double n2)
{
return n1+n2;
}
}
最后将结构体作为泛型参数传递给类型:
public class Matrix<T,TArithmetic>
where TArithmetic:struct, IArithmetic<T>
{
private static readonly TArithmetic arithmetic=new TArithmetic();
void DoStuff()
{
arithmetic.Add(1,2);
}
}
我没有对它进行基准测试,但我怀疑它相当快,因为泛型对于传递给它的每个值类型都是专门化的。这就是为什么DoubleArithmetic
是struct
。