2016-01-20 58 views
1

我打算詢問泛型操作符+重載,但不是在典型的「我可以操作符+泛型類型」方式。C#泛型操作符 - RTTI方法

問題是在底部

最近,我開始在C#創建矩陣類,並經過一段時間它來找我,我不能做簡單的T + T!

因此,我使用Google搜索並找到了幾種解決方法。

  1. 創建表達link
  2. 創建抽象類abstract class Matrix<T>{//some code}。創建「受保護的虛擬方法Add(T itemToAdd)」,然後創建 操作是這樣的:T operator+(T item1, T item2){return item1.Add(item2);}(堆棧的大部分信息),然後繼承這個方法class Matrix : Matrix<int>here
  3. 使用方法添加如:T Add(T first, T second){ dynamic output = first + second; return output;}(堆棧某處)

首先一個就是不適合我,所以我想第二個,但後來我跑進嚴重的問題,如:

  1. 但卻難免重複碼(的很多) - 我創造了4類:INT,雙,長,複雜 - 我自己的類型
  2. 創建多個擴展方法等。

第三個是如此不安全,我拒絕它immidietlay。

在我掙扎之後,我意識到:'爲什麼我不使用RTTI和反射?我知道,它在運行時很昂貴,但爲什麼不使用靜態構造函數來執行此操作?

這裏是我的想法(僞):

class Matrix<T>{ 
    static Func<T,T,T> Add; 
    static Matrix 
    { 
    if(T is int) 
     Add = (first,second) = > ((int)first)+((int)second); 
    else if(T is long) 
     Add = (first,second) = > ((long)first)+((long)second); 
    // and so on for built-in types 
    else 
    { // T is not built-in type 
    if(typeof(T).GetMethods().Contains("op_Addition")) 
    { 
     Add = (first,second) => typeof(T).getMethod("op_Addition").invoke(first,second); 
    } 
    } 
} 

我知道反思是昂貴的,但它會做的只有一次(每類)! 你開始爭論前:我要編寫T is int這樣的:

var type = typeof(T); 
if(type==typeof(int)) // code 

我知道,我不能明確地轉換噸至詮釋,但必須有某種形式的「解決」。問題在於(例如)Int32沒有明確的'操作符'方法,因此反射並沒有多大用處。

所有的介紹後我有兩個問題:

  1. 它是一個很好的方法還是你看到它大的紕漏?
  2. 它可行嗎?我不想在不知道我的lambda函數是否可以工作的情況下開始創建代碼。

編輯1 + 2 我改變了我的代碼一般。 我想,也許你需要我的類的用法,在這裏你是:

Matrix<int> matrix = new Matrix(1,1); // creates int-based matrix 
Matrix<MyClass> matrix2 = new Matrix(1,1); // creates some other type matrix 

ANSWER基於dasblinkenlight的答案 我成功地做到這一點:

public interface ITypeTratis<T> 
    { 
     T Add(T a, T b); 
     T Mul(T a, T b); 
     T Sub(T a, T b); 
     T Div(T a, T b); 
     bool Eq(T a, T b); 
    } 

    public class IntTypeTratis : ITypeTratis<int> 
    { 
     //code for int 
    } 
    public class DoubleTypeTratis : ITypeTratis<double> 
    { 
     //code for double 
    } 
internal class TypeTraits<T> : ITypeTratis<T> 
{ 
    public Func<T, T, T> AddF; 
    public Func<T, T, T> MulF; 
    public Func<T, T, T> DivF; 
    public Func<T, T, T> SubF; 
    public Func<T, T, bool> EqF; 
    public T Add(T a, T b) => AddF(a, b); 

    public bool Eq(T a, T b) => EqF(a, b); 

    public T Mul(T a, T b) => MulF(a, b); 

    public T Sub(T a, T b) => SubF(a, b); 

    public T Div(T a, T b) => DivF(a, b); 
} 
public class Matrix<T> 
    { 
     private static IDictionary<Type, object> traitByType = new Dictionary<Type, object>() 
     { 
      {typeof (int), new IntTypeTratis()}, 
      {typeof (double), new DoubleTypeTratis()} 
     }; 
     static Matrix() 
     { 
      Debug.WriteLine("Robie konstruktor dla " + typeof(T)); 
      var type = typeof(T); 
      if (!traitByType.ContainsKey(type)) 
      { 
       MethodInfo add, sub, mul, div, eq; 
       if ((add = type.GetMethod("op_Addition")) == null) 
        throw new NotSupportedException("Addition is not implemented"); 
       if ((sub = type.GetMethod("op_Subtraction")) == null) 
        throw new NotSupportedException("Substraction is not implemented"); 
       if ((mul = type.GetMethod("op_Multiply")) == null) 
        throw new NotSupportedException("Multiply is not implemented"); 
       if ((div = type.GetMethod("op_Division")) == null) 
        throw new NotSupportedException("Division is not implemented"); 
       if ((eq = type.GetMethod("op_Equality")) == null) 
        throw new NotSupportedException("Equality is not implemented"); 
       var obj = new TypeTraits<T> 
       { 
        AddF = (a, b) => (T)add.Invoke(null, new object[] { a, b }), 
        SubF = (a, b) => (T)sub.Invoke(null, new object[] { a, b }), 
        MulF = (a, b) => (T)mul.Invoke(null, new object[] { a, b }), 
        DivF = (a, b) => (T)div.Invoke(null, new object[] { a, b }), 
        EqF = (a, b) => (bool)eq.Invoke(null, new object[] { a, b }) 
       }; 
       traitByType[type] = obj; 

      } 
     } 
} 

而這正是我在尋找。

+0

如果有任何不清楚的地方,請隨時提出任何問題。這不是某種家庭作業 - 只要我用C#開心,但我真的想創造出好的代碼。 – MaLiN2223

+0

你是在做這個練習嗎?或者你只是需要數學矩陣運算來做其他事情? –

+0

在我看來,你可以從某個庫中獲得這種功能 –

回答

2

是的,你的方法可以正常工作。

您的靜態構造函數將爲每個類型參數T運行,確保Add設置正確。

您可能想要將附加邏輯分離到矩陣外部的單獨類中,然後使用該類根據矩陣的類型運行操作。例如,如果您還需要乘法,你可以建立一個ITypeTraits<T>接口,具有AddMultiply

public interface ITypeTraits<T> { 
    T Add(T a, T b); 
    T Mul(T a, T b); 
} 

現在可以生成各種型號,例如的ITypeTraits<T>實現

public class IntTypeTraits : ITypeTraits<int> { 
    public int Add(int a, int b) { return a+b; } 
    public int Mul(int a, int b) { return a*b; } 
} 
public class LongTypeTraits : ITypeTraits<long> { 
    public long Add(long a, long b) { return a+b; } 
    public long Mul(long a, long b) { return a*b; } 
} 
... // and so on 

做一本字典了他們

static readonly IDictionary<Type,object> traitByType = new Dictionary<Type,object> { 
    {typeof(int), new IntTypeTraits() } 
, {typeof(long), new LongTypeTraits() } 
... // and so on 
}; 

,並得到您需要執行操作之一:

ITypeTraits<T> traits = (ITypeTraits<T>)traitByType(typeof(T)); 
T first = ... 
T second = ... 
T sum = traits.Add(first, second); 
T prod = traits.Mul(first, second); 
+0

非常抱歉,我忘記了在我的代碼中添加! 此修改後,您的答案是否相同? – MaLiN2223

+0

@Malin不,它不會是一樣的。這是我在一個項目中成功使用的方法,我需要做一些類似你的項目。我使用類來代替lambda表達式,並避免了條件和表演。 – dasblinkenlight

+0

我寫了一個非常類似的方法,但沒有字典。在下面檢查。 –

0

什麼是錯的#3?您可以檢查類型,如下所示:

public abstract class Matrix<T> 
{ 
    public static HashSet<Type> AllowAdd = new HashSet<Type> 
    { 
     typeof(int), 
     typeof(long), 
     typeof(string), 
     typeof(double), 
    }; 

    public T Add<T>(T first, T second) 
    { 
     if(!AllowAdd.Contains(typeof(T))) 
     { 
      throw new Exception(string.Format("Cannot preform addition for type: {0}", typeof(T).Name)); 
     } 

     dynamic result = (dynamic)first + (dynamic)second; 
     return (T)result; 
    } 
} 
0

dasblinkenlight's answer上構建,這是我的版本。好處是它不需要字典查找,而是使類型系統執行它。我認爲應該更快,但我還沒有衡量它。也少一點打字。

public abstract class MatrixBase 
{ 
    protected static class OperationDict<T> 
    { 
     private static Func<T,T,T> _notSupported = (a, b) => { throw new NotSupportedException(string.Format("Type {0} not supported for Matrix operations!", typeof(T))); }; 

     public static Func<T, T, T> Add = _notSupported; 
     public static Func<T, T, T> Multiply = _notSupported; 
    } 

    static MatrixBase() 
    { 
     OperationDict<int>.Add = (a, b) => a + b; 
     OperationDict<int>.Multiply = (a, b) => a * b; 

     OperationDict<decimal>.Add = (a, b) => a + b; 
     OperationDict<decimal>.Multiply = (a, b) => a * b; 

     // Etc. for all supported types 

    } 
} 
public class Matrix<T> : MatrixBase 
{ 
    public T DoAdd(T a, T b) 
    { 
     return OperationDict<T>.Add(a, b); 
    } 
} 
+0

他提到了構建一個庫,爲了在包含+運算符的自定義類上使用此代碼,需要庫的用戶更改MatrixBase的實現 –

+0

@EduardoWada - 那麼,他可以添加一個擴展點,使用該庫的用戶可以提供這些功能。而且,如果他能找出一種方法來產生這些功能,那麼也可以將其納入。 –

0

我認爲你是在正確的道路上,爲了避免使用反射,則需要以某種方式通知你知道「T」有「+」操作符編譯器,但是,this feature does not yet exist在C#中,所以這是不可能的,沒有運行時類型檢查或施加其他約束。

如果你不關心性能,你可以使用dynamic

(dynamic)first + (dynamic)second 

但這需要在每一個操作

幾個反射性能命中或者你可以使用一些其它更復雜的方法在字典中緩存特定的方法,但在add的實現中,您至少不會撥打至少.GetType()