C#泛型运算符 - RTTI方法

时间:2016-01-20 21:47:17

标签: c# generics design-patterns reflection rtti

我打算询问有关泛型运算符+重载但不是典型的"我可以执行operator + for generic type"方式。

问题在底部

我最近开始在C#中创建矩阵类,过了一会儿我觉得我不能做简单的T + T!

因此,我用谷歌搜索并搜索了几个变通方法。

  1. 创建表达式link
  2. 创建抽象类abstract class Matrix<T>{//some code}。创建受保护的虚拟方法Add(T itemToAdd)&#39;然后创建 这样的运算符:T operator+(T item1, T item2){return item1.Add(item2);}(堆栈上的大多数帖子)然后在class Matrix : Matrix<int> here
  3. 中继承此方法
  4. 使用方法添加如:T Add(T first, T second){ dynamic output = first + second; return output;}(堆栈中的某个位置)
  5. 第一个只是不适合我,所以我尝试了第二个,但后来我遇到了严重的问题,如:

    1. (很多)重复代码 - 我创建了4个类:int,double,long,Complex - 我自己的类型
    2. 创建多种扩展方法等。
    3. 第三个是如此不安全,我拒绝了它immidietlay。

      在我挣扎之后,我意识到:&#39;为什么我不使用RTTI和反思?&#39;我知道,它的运行时间很昂贵,但为什么不使用静态构造函数呢?

      这是我的想法(伪代码):

      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
      

      我知道我无法将T显式转换为int,但必须有某种解决方法。问题是(例如)Int32没有明确的方法&#39;对于操作员+因此,反射没有多大用处。

      在完成所有介绍后,我有两个问题:

      1. 这是一个好方法还是你看到了重大缺陷?
      2. 可行吗?我不想在不确定我的lambda函数能否正常工作的情况下开始创建代码。
      3. 编辑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;
        
                    }
                }
        }
        

        这正是我所寻找的。

4 个答案:

答案 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);

答案 1 :(得分: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;
    }
}

答案 2 :(得分:0)

dasblinkenlight's answer上的Bulding,这是我的版本。好处是它不需要字典查找,而是使类型系统执行它。我想应该更快,但我还没有测量过。打字也少一点。

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);
    }
}

答案 3 :(得分:0)

我认为你是在正确的道路上,为了避免使用反射,你需要以某种方式通知编译器你知道&#34; T&#34;有&#34; +&#34;但是,C#中的this feature does not yet exist运算符,因此如果没有运行时类型检查或强加其他约束,则无法实现。

如果您不关心效果,可以使用dynamic

(dynamic)first + (dynamic)second

但是每次操作都需要多次反映性能

或者你可以使用一些其他更复杂的方法来缓存字典中的特定方法,但是你在.GetType()的实现中至少忘记调用add