簡體   English   中英

通用運算符的有效方法

[英]Valid approach for generic operators

我有一個Gen<T>類,我希望能夠比較它們。 以下代碼無法編譯,因為==無法應用於Parent和Child。 有沒有辦法使這種比較成為可能,或者一般來說這是不好的做法?

public class Parent{
    public int x;
}

public class Child:Parent{}

public class Gen<T> 
    where T : Parent 
{
    public T variable;
}

public static class Gen
{
    public static bool operator ==(Gen<Parent> left, Gen<Parent> right){
        if (left.variable.x == right.variable.x)
            return true;
        else
            return false;
    }
}

public void Test()
{
    Gen<Parent> foo = new Gen<Parent>();
    Gen<Child> bar = new Gen<Child>();

    if (foo == bar)
    {
        ...
    }
}

完整的上下文如下:

  1. Gen<T>等於ColorSet<T> ,其中T:Color
  2. 父等於顏色
  3. Child是一個存儲顏色附加信息的類,對於每個Color對象都不是必需的。

我想通過ColorSet<T>類訪問每個Color ,如下所示:

public class ColorSet<T> where T : Color
{
     private T blue;
     private T red;
     private T green;

     public ColorSet()
     {
         Red = (T)Activator.CreateInstance(typeof(T), new object[] { });
         Red.Name = Values.Res.get("red");
         Blue = (T)Activator.CreateInstance(typeof(T), new object[] { });
         Blue.Name = Values.Res.get("blue");
         Green = (T)Activator.CreateInstance(typeof(T), new object[] { });
         Green.Name = Values.Res.get("green");
     }
}

但有時候我需要ColorSet<Color> ,有時候需要ColorSet<Child>來獲取更多信息。 並且應該可以將ColorSet<Color>ColorSet<Child>進行比較,因為它們具有最相關的相關信息。

回到你原來的問題/樣本:這不是很漂亮,但它有效(對你的例子 - 我只測試了兩個)它雖然使用反射,所以我對它不滿意:

public class Parent
{
    public int x;

    public Parent (int x)
    {
        this.x = x;
    }

    public override bool Equals(object o)
    {
        var p = o as Parent;
        if (object.Equals(p, null))
            return false;

        return this.x == p.x;
    }

    public override int GetHashCode()
    {
        return x;
    }

    public static bool operator ==(Parent a, Parent b)
    {
        return a.Equals (b);
    }

    public static bool operator !=(Parent a, Parent b)
    {
        return !(a == b);
    }

}

public class Child : Parent
{
    public Child (int x)
        : base(x)
    {

    }
}

public class Gen<T> 
    where T : Parent 
{
    public T variable;

    public Gen (T x)
    {
        this.variable = x;
    }

    public override bool Equals(object o)
    {
        if (object.Equal(o, null)) return false;

        // CAUTION: VERY DIRTY - just a quick reply to hvd - should check/remove this with test cases!
        try
        {
           var oT = o.GetType ().GetGenericTypeDefinition ();
           var tT = this.GetType ().GetGenericTypeDefinition ();
           if (tT != oT)
               return false;

           // for example this:
           // var oVar = o.GetType().GetField ("variable").GetValue (o);
           // should really be
           var varField = o.GetType().GetField("variable");
           if (varField == null) return;
           var oVar = varField.GetValue(o);

           if (object.Equals(oVar, null)) 
              return object.Equals(this.variable, null);

           return this.variable.Equals (oVar);
         } catch { return false; }
    }

    public override int GetHashCode()
    {
        return variable.GetHashCode();
    }

    public static bool operator ==(Gen<T> a, object b)
    {
        return a.Equals (b);
    }

    public static bool operator !=(Gen<T> a, object b)
    {
        return !(a == b);
    }

}

這是你的另一個例子:

public static void Test()
{
    Gen<Parent> foo = new Gen<Parent>(new Parent(5));
    Gen<Child> bar = new Gen<Child>(new Child(5));
    Gen<Child> bas = new Gen<Child>(new Child(6));

    if (foo == bar)
        Console.WriteLine ("equal");
    else
        Console.WriteLine ("not-equal");

    if (foo == bas)
        Console.WriteLine ("equal");
    else
        Console.WriteLine ("not-equal");
}

順便說一下:你真的不需要Parent類的(==)和(!=) - 但它不會傷害它

(從評論中擴展)通用類似乎沒有必要。 讓運算符為泛型類型工作的有效方法是重新處理類型,使它們不再是通用的。

ColorSet可以定義為

public class ColorSet {
  private Color red;
  private Color green;
  private Color blue;

  protected ColorSet(Type type) {
    red = (Color)Activator.CreateType(type);
    red.Name = Values.Res.get("red");
    green = (Color)Activator.CreateType(type);
    green.Name = Values.Res.get("red");
    blue = (Color)Activator.CreateType(type);
    blue.Name = Values.Res.get("red");
  }

  public static ColorSet FromType<T>() where T : Color {
    return new ColorSet(typeof(T));
  }
}

現在,您將調用ColorSet.FromType<ExtendedColor>()而不是new ColorSet<ExtendedColor>() ColorSet.FromType<ExtendedColor>()

只要您不需要在構造函數之外使用T ,這就可以工作。

例如,如果你有一個

public T Red { get { return red; } }

屬性,你需要將其更改為

public Color Red { get { return red; } }

屬性。

但是,如果您有類似的內容,並且確實希望保留泛型類型,則可以將其放在派生的泛型類中:

public class ColorSet<T> : ColorSet where T : Color {
  public ColorSet<T>() : base(typeof(T)) { }
  public new T Red { get { return (T)base.Red; } }
}

它仍然只需要基礎非泛型ColorSet類的運算符。

public class IGen<out T> 
    where T : Parent 
{
    T Variable{ get; }
}

public class Gen<T>
    : IGen<T>
    where T : Parent 
{
    public T Variable {get;set;}

    private static Func<T, T, bool> _equal;

    static Gen()
    {
        var left = Expression.Parameter(typeof(T));
        var right = Expression.Parameter(typeof(T));
        var body = Expression.Equal(left, right);
        var lambda = Expression.Lambda<Func<T, T, bool>>(body, left, right);
        _equal = lambda.Compile();
    }

    public static bool operator ==(Gen<T> left, Gen<T> right)
    {
        return _equal(left.Variable, right.Variable);
    }


    public static bool operator ==(Gen<T> left, IGen<T> right)
    {
        return _equal(left.Variable, right.Variable);
    }


    public static bool operator ==(IGen<T> left, Gen<T> right)
    {
        return _equal(left.Variable, right.Variable);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM