定义一个通用类型,实现+运算符。

13
我正在解决一个问题,目前它适用于int类型,但我希望它适用于所有可以使用+运算符添加的类。在泛型中有没有定义这个的方法?例如:

可能是重复的问题:
解决.NET泛型中的重载运算符约束的方案

public List<T> Foo<T>() where T : ISummable

有没有办法做到这一点?
编辑:
将委托传递给执行求和而不是使用类型为Int的+=时,性能最多慢了540%。调查可能的其他解决方案
最终解决方案:
感谢大家的建议。我最终采用了一种既不太慢又在编译时强制检查的解决方案。我不能完全归功于此,因为一位同事帮助我达成了这个目标。无论如何,这就是它:
实现一个接口,其中包含所有所需的运算符,形式为函数。
public interface IFoo<InputType, OutputType>
{
    //Adds A to B and returns a value of type OutputType
    OutputType Add(InputType a, InputType b);
    //Subtracts A from B and returns a value of type OutputType
    OutputType Subtract(InputType a, InputType b);
}

创建你想要定义的类,但是不要使用Where子句,而是使用IFoo接口的依赖注入实例。输出类型通常为double,因为操作的本质是数学的。
public class Bar<T>
{
    private readonly IFoo<T,double> _operators;

    public Bar(IFoo<T, double> operators)
    {
        _operators = operators;
    }
}

现在当你使用这个类时,你可以像这样定义操作规则:
private class Foo : IFoo<int, double>
{
    public double Add(int a, int b)
    {
        return (double)(a+b);
    }
    public double Subtract(int a, int b)
    {
        return (double)(a-b);
    }
}

那么您可以像这样使用它:
Foo inttoDoubleOperations = new Foo();
Bar myClass = new Bar(Foo);

那样所有操作都在编译时执行 :)
享受吧!

1
不,直接是不行的。然而,在CodeProject上有一篇模拟这个的文章。 - leppie
Vinay,请查看Bart的评论以获取你问题的答案。如果我实现了自己的接口叫做ISummable,我就不能让Ints等工作,因为它们没有实现那个接口。 - TerrorAustralis
@Ieppie:你是指使用泛型进行运算符重载吗?这已经过时了:现在,你可以使用dynamic更轻松地完成相同的操作,并且它是解决手头问题最不实用的方法。 - Timwi
1
@Timwi:除了动态的速度会慢很多这个事实之外,它并不过时。 - leppie
1
@TerrorAustralis:如果我手头有URL,我就会把它发布出来。我记得几年前看到过它,并不意味着我仍然拥有这个链接。 - leppie
显示剩余4条评论
2个回答

11

这是一个关于C#的常见需求:希望能够指定更多通用的参数约束,而不仅限于已有的一些。其中运算符也是最常被要求的。然而,目前C#还不支持这个功能。

可能的解决方法:

  • Pass a delegate to any method that needs to do addition. This is the most type-safe option, but of course it’s annoying if you need to call such a method often. For example:

    public class Generic<T> {
        public void DoSomething(T anItem, T anotherItem, Func<T, T, T> add) {
            // instead of
            Blah(anItem + anotherItem);
            // have to write:
            Blah(add(anItem, anotherItem));
        }
    }
    
    Generic<int> genInt = ...;
    // and then instead of ...
    genInt.DoSomething(1, 2);
    // have to write:
    genInt.DoSomething(1, 2, (a, b) => a + b);
    
  • Declare your own interface IAddable. Then you can use it as a generic type parameter constraint, but obviously you can’t use int as the parameter then. You would have to use a struct of your own that contains just an int and which implements IAddable:

    public interface IAddable<T> {
        T Add(T other);
    }
     
    public struct Integer : IAddable<Integer> {
        public int Value;
        public Integer(int value) { Value = value; }
        public Integer Add(Integer other) { return new Integer(Value + other.Value); }
    }
    
    // then instead of
    Generic<int> blah = ...;
    // have to write:
    Generic<Integer> blah = ...;
    
  • dynamic. Another possible workaround is to use dynamic, but this is rather hacky and completely unsafe: it will let you pass in any type and call any method or operator, and only crash at runtime, not at compile-time.


谢谢,我可能会采用传递委托的方法。这似乎是一个合理的解决方法,并不会给用户增加太多的工作量。谢谢老兄! - TerrorAustralis

3
在C# 4.0中,有一个新的关键字dynamic,它允许您在运行时执行此操作。在以前的版本中,这是可能的,但对我来说过于模糊和棘手。但是,您始终可以传递一个委托,在泛型内部执行加法。否则不可能,因为没有ISummable,IAdditive,一般来说,在编译时无法知道某个东西是否具有可加性*。除非您自己创建IAdditive并将其标记为某些类型,但这对于int等类型不起作用。
敬礼。

谢谢您的回复!您对于 IAdditive 的评论正中了要害。我无法将其用于 int 和其他预实现的类/结构体。 - TerrorAustralis

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