如何在C#中对通用数字进行求和?

6

如下所示的代码中,我需要计算两个通用数值的总和。

可能有重复:
C#仅限整数的通用约束


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等)之外的任何其他内容,那么我该如何执行求和操作呢?(效率是需要考虑的因素)

1
该问题被关闭为寻求对T施加约束的重复问题,而不是本问题所尝试做的。我认为应该重新开放此问题。 - Sergey Kalinichenko
4个回答

11
您可以使用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); } }

唯一的缺点是,即使使用不支持加法的类型T实例化NumberContainer,这个代码也可以编译;当然,在运行时它会抛出异常。一个额外的好处是,这应该可以使用用户定义的+操作符。

7

除了使用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是某个不支持+的随机结构体,则可能会出现异常(我认为是这样)。


动态使用有点可怕,特别是考虑到运算符不是虚拟的...我猜它会通过它的魔法盒子“自动工作”。(我更喜欢结构类型也一样 :-/) - user166390
@pst - 我同意,但它确实可以工作。不过我理解你的观点。 - M.Babcock

0

您可以为 T 指定一个约束,使其必须是一个结构体,并且可能实现了 I

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是整数或浮点类型。不过,您可以在运行时检查并抛出异常。

我凭记忆修改并编译了它。如果您想进一步阐述为什么它不起作用,那将更有帮助。 - moribvndvs
DateTime是一个结构体和IConvertible,但不能转换为double,因此每当您使用DateTime参数调用它时,它将抛出异常。 - brainfood

0
您可以将其视为抽象基类,并派生指定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;
    }
}

1
如果一个 int 可以转换为 T,那么这就不是问题了。你的代码无法编译。 - M.Babcock
我真傻。更新答案,提供另一种解决方案,因为现在有几个使用动态和链接的解决方案。 - Michael Christensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接