通用C#代码和Plus运算符

我正在写一个类,对C#中的每个基本数字types进行基本相同的计算。 虽然实际计算更为复杂,但将其视为计算多个值的平均值的一种方法

class Calc { public int Count { get; private set; } public int Total { get; private set; } public int Average { get { return Count / Total; } } public int AddDataPoint(int data) { Total += data; Count++; } } 

现在要支持double,float和定义operator +和operator /的其他类的相同操作,我的第一个想法就是简单地使用generics:

 class Calc<T> { public T Count { get; private set; } public T Total { get; private set; } public T Average { get { return Count / Total; } } public T AddDataPoint(T data) { Total += data; Count++; } } 

不幸的是,C#无法确定T是否支持运算符+,因此不能编译上面的代码片段。 我的下一个想法是将T约束为支持这些运营商的types,但是我最初的研究表明这是不能做到的。

在一个实现了自定义接口的类中,我想要支持的每个types都是可能的,比如IMath,并且限制T,但是这个代码会被调用很多次,我想避免装箱开销。

有没有一个优雅和有效的方法来解决这个没有代码重复?

我结束了使用expression式,马克·格雷韦尔概述了一种方法,我发现通过链接closuresspinon的评论。

http://www.yoda.arachsys.com/csharp/genericoperators.html

(对不起,如果我今天发布,但我正在寻找一个地方放这段代码,这个问题似乎是完美的)

作为Gravell文章的扩展:

 public static class Add<T> { public static readonly Func<T, T, T> Do; static Add() { var par1 = Expression.Parameter(typeof(T)); var par2 = Expression.Parameter(typeof(T)); var add = Expression.Add(par1, par2); Do = Expression.Lambda<Func<T, T, T>>(add, par1, par2).Compile(); } } 

你使用它像:

 int sum = Add<int>.Do(x, y); 

好处是我们使用.NET的types系统来保存Add的各种“变体”,并在必要时创build新的变体。 所以当你第一次调用Add<int>.Do(...)Expression将会被创build,但是如果你第二次调用它, Add<int>将会被完全初始化。

在一些简单的基准testing中,它比直接加法慢2倍。 我觉得这很好。 啊…它与重新定义operator+对象兼容。 清楚地build立其他操作是容易的。

从Meirion休斯join

方法可以用元编码进行扩展,所以你可以处理T1 操作 T2 。 例如,在这里,如果T1是一个数字,那么在operator *之前,它需要先转换为T2 == double然后再转换回来。 而当T1Foo并且Foo有一个T2 == double乘法运算符时,可以省略转换。 trycatch是必须的,因为它是检查T operator *(T, double)是否存在的最简单方法。

 public static class Scale<T> { public static Func<T, double, T> Do { get; private set; } static Scale() { var par1 = Expression.Parameter(typeof(T)); var par2 = Expression.Parameter(typeof(double)); try { Do = Expression .Lambda<Func<T, double, T>>( Expression.Multiply(par1, par2), par1, par2) .Compile(); } catch { Do = Expression .Lambda<Func<T, double, T>>( Expression.Convert( Expression.Multiply( Expression.Convert(par1, typeof (double)), par2), typeof(T)), par1, par2) .Compile(); } } } 

在C#4.0中有一种使用dynamic的方法,但它不是很完美,但它可以带来新的亮点。

细节在这个博客文章

我发现了另一个有趣的方法,比我最初使用的expression式树解决scheme更容易编码和debugging:

http://www.codeproject.com/KB/cs/genericnumerics.aspx

该解决scheme以有趣的方式使用generics约束来确保支持所有必需的操作,但不引入任何装箱或虚拟方法调用。