繁体   English   中英

C# 通用运算符

[英]C# Generic Operators

我正在尝试像这样实现一个通用运算符:

class Foo
{
   public static T operator +<T>(T a, T b) 
   {
       // Do something with a and b that makes sense for operator + here
   }
}

我真正想做的是优雅地处理 inheritance。在 Foo 中使用标准运算符 +,其中 T 代替“Foo”,如果任何人都派生自 Foo(比如 Bar 继承 Foo),那么 Bar + Bar 操作仍然会返回一个 Foo。 我希望用通用运算符 + 来解决这个问题,但我只是得到上面的语法错误(在 <)让我相信这样的代码是不合法的。

有没有办法制作通用运算符?

不,您不能在C#中声明泛型运算符。

运算符和继承并不能很好地融合在一起。

如果希望Foo + Foo返回Foo,而Bar + Bar返回Bar,则需要在每个类上定义一个运算符。 但是,由于运算符是静态的,您将无法获得多态的好处,因为要调用的运算符将在编译时确定:

Foo x = new Bar();
Foo y = new Bar();
var z = x + y; // calls Foo.operator+;

https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html

static T Add<T>(T a, T b) {
    //TODO: re-use delegate!
    // declare the parameters
    ParameterExpression paramA = Expression.Parameter(typeof(T), "a"),
        paramB = Expression.Parameter(typeof(T), "b");
    // add the parameters together
    BinaryExpression body = Expression.Add(paramA, paramB);
    // compile it
    Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
    // call it
    return add(a,b);       
}

您可以在通用类Foo中定义运算符。

您也可以创建真正的泛型运算符,但C#编译器不会使用它们。

[System.Runtime.CompilerServices.SpecialName]
public static T op_Addition<T>(T a, T b) { ... }

您不能在C#中声明泛型运算符-我不确定推理的原因,但认为这对于实现团队来说是有用与否的事情(我相信Jon Skeet可能会在此处发表文章,或者当他发表时在他的博客中讨论了他想在C#中看到的东西)。

实际上,您甚至不能在C#中将运算符与泛型一起使用

这是因为泛型必须适用于可能提供的所有可能的类型。 这就是为什么当要使用==时,必须将泛型类型的作用域限定为类,如下所示:

void IsEqual<T>(T x, T y) where T : class
{
    return x == y;
}

不幸的是,您不能执行以下操作:

void Add<T>(T x, T y)  where T : operator +
{
    return x + y;
}

您可能也对我遇到的这篇简短的摘要文章感兴趣。

正在寻找相同的东西,谷歌将我带到了这里。...我对接受的答案不太满意,正在寻找解决方法。

我设法使用泛型实现了这一点。 这是Foo and Bar类:

    class Foo
    {
        private int value;

        public Foo(int x)
        {
            value = x;
        }
        public virtual int getVal()
        {
            return value;
        }
    }

    class Bar : Foo
    {
        private int derivedValue;

        public Bar(int x):base(x) 
        {
            derivedValue = x;
        }
        public override int getVal()
        {
            return derivedValue;
        }
    }

然后是一个通用类,其中包含运算符,但仅限于Foo的类型并派生自Foo:

    class GenericOp<T> where T : Foo
    {
        private T value;

        public GenericOp(T x)
        {
            value = x;
        }
        public static Foo operator +(GenericOp<T> a, GenericOp<T> b) 
        {
            return new Foo(a.value.getVal() + b.value.getVal());
        }
    }

一些用法代码向您显示总是返回Foo并防止您混合类型:

Foo f1 = new Foo(1);
Foo f2 = new Foo(2);
Bar b1 = new Bar(10);
Bar b2 = new Bar(20);

GenericOp<Foo> left = new GenericOp<Foo>(f1);
GenericOp<Foo> right = new GenericOp<Foo>(f2);
Foo res = left + right;

GenericOp<Bar> left1 = new GenericOp<Bar>(b1);
GenericOp<Bar> right1 = new GenericOp<Bar>(b2);
Foo res1 = left1 + right1;

GenericOp<Foo> left2 = new GenericOp<Foo>(f1);
GenericOp<Bar> right2 = new GenericOp<Bar>(b1);
//Foo res2 = left2 + right2; //this fails and rightfully so.

现在可以在 C# 11 通过static abstract接口方法; 例如:

public interface IMyInterface<T> where T : IMyInterface<T>
{
    static abstract T operator +(T left, T right);
}

然后您可以通过where T: IMyInterface<T>将其用作:

class Bar
{
    static T Add<T>(T x, T y, T z) where T : IMyInterface<T>
    {
        return x + y + z;
    }
}

但是,问题是您想要的每个T都需要实现IMyInterface<T> ,这对于您无法控制的预定义类型(如intfloat等)是不可能的。 好消息是 .NET 7会为您完成此操作,适用于您可能想到的所有类型,因此实际上您不需要定义自己的 API; 相反,您可以使用系统定义的接口INumber<T>

    static T Add<T>(T x, T y, T z) where T : INumber<T>
    {
        return x + y + z;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM