如何在C#中汇总通用数字?

时间:2012-02-18 03:58:44

标签: c# generics

  

可能重复:
  C# generic constraint for only integers

正如您在下面的代码中看到的,我需要计算两个通用数字的总和。

public class NumberContainer<T>
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public T Total { get { return ValueA + ValueB; } }
}

但是,不可能直接添加两个T值,这会导致编译器错误:

  

运算符'+'不能应用于'T'和'T'类型的操作数

鉴于我不打算将T用于表示数字的值类型(short,ushort,int,uint等)以外的任何其他内容,我怎么能执行总和呢? (效率是一个需要考虑的因素)

4 个答案:

答案 0 :(得分:12)

你可以用LINQ中的“小魔法”来做到这一点:

private static readonly Func<T, T, T> adder;
static NumberContainer() {
    var p1 = Expression.Parameter(typeof (T));
    var p2 = Expression.Parameter(typeof (T));
    adder = (Func<T, T, T>)Expression
        .Lambda(Expression.Add(p1, p2), p1, p2)
        .Compile();
} 
public T Total { get { return adder(ValueA, ValueB); } }

唯一的缺点是,即使NumberContainer使用不支持添加的类型T进行实例化,此代码也将编译;当然它会在运行时抛出异常。另一个好处是,应该与用户定义的+运算符一起使用。

答案 1 :(得分:6)

除了使用LINQ之外,如果你使用的是.NET 4,可以使用dynamic

static T Add<T>(T x, T y) 
{
    dynamic dx = x, dy = y;
    return dx + dy;
}

在您的代码示例中,这将变为:

public class NumberContainer<T> where T: struct
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public T Total { get { return ((dynamic)ValueA) + ((dynamic)ValueB); } }
}

这种方法并不能确保安全。如果T是一些不支持+的随机结构,那么您最终会遇到异常(我认为)。

答案 2 :(得分:1)

您可以为T指定一个约束,它必须是一个结构,也许它实现了IConvertable,然后使用Convert

public class NumberContainer<T>
        where T : struct, IConvertible
    {
        public T ValueA { get; private set; }
        public T ValueB { get; private set; }
        public T Total {
            get
            {
                // do type checking here, then:

                return (T)Convert.ChangeType(
                    Convert.ToDouble((object)ValueA) +
                    Convert.ToDouble((object)ValueB), typeof(T));
            } 
        }
    }

然而,泛型无法保证在编译时T是整数或浮点类型。但是,您可以在运行时检查它,并抛出异常。

答案 3 :(得分:1)

您可以将此视为抽象基类,并派生指定T值的特定类并提供Total的实现

public abstract class NumberContainer<T>
    where T: struct
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public abstract T Total();
}

public class IntContainer : NumberContainer<int>
{
    public override int Total()
    {
        return ValueA + ValueB;
    }
}