我正在编写一个类,它对C#中的每个原始数字类型进行基本相同类型的计算.虽然实际计算更复杂,但可以将其视为计算多个值的平均值的方法,例如
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++;
}
}
Run Code Online (Sandbox Code Playgroud)
现在支持double,float和其他定义operator +和operator /的类的相同操作,我首先想到的只是使用泛型:
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++;
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,C#无法确定T是否支持运算符+和/所以不编译上述代码段.我的下一个想法是将T限制为支持这些运算符的类型,但我的初步研究表明无法做到这一点.
当然可以在实现自定义接口的类中包含我想要支持的每个类型,例如IMath和限制T,但是这个代码将被调用很多次,我想避免装箱开销.
有没有一种优雅而有效的方法来解决这个没有代码重复?
Eri*_* J. 14
我最终使用了表达式,这是Marc Gravell概述的一种方法,我通过以下链接发现了spinon的评论.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它:
int sum = Add<int>.Do(x, y);
Run Code Online (Sandbox Code Playgroud)
优点是我们使用 .NET 的类型系统来保存各种“变体”Add并在必要时创建新的“变体”。因此,第一次调用Add<int>.Do(...)将Expression被构建,但如果你第二次调用它,Add<int>将已经完全初始化。
在一些简单的基准测试中,它比直接相加慢 2 倍。我认为这非常好。啊...它与重新定义operator+. 显然,构建其他操作很容易。
梅里恩·休斯的补充
可以使用元编码来扩展方法,以便您可以处理T1 操作 T2的情况。例如,这里如果T1是一个数字,那么需要T2 == double先将其转换为operator *数字,然后再将其转换回来。而当T1isFoo和Foohas 运算符与 a 相乘时,T2 == double您可以省略转换。,是必要的try,catch因为这是检查 是否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();
}
}
}
Run Code Online (Sandbox Code Playgroud)