簡體   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