3

I intend to ask about generic operator+ overloading but not in typical "can I do operator+ for generic type" way.

Questions are on the bottom

I recently started to create matrix class in C# and after a while It came to me that I cannot do simple T + T !

Thus, I googled and googled and found several workarounds.

  1. Create Expression link
  2. Create abstract class abstract class Matrix<T>{//some code}. Create 'protected virtual method Add(T itemToAdd)' and then create operator like this : T operator+(T item1, T item2){return item1.Add(item2);}(most posts on stack) and then inherit this method in class Matrix : Matrix<int> here
  3. Use method Add such as : T Add(T first, T second){ dynamic output = first + second; return output;} (somewhere on stack)

First one just does not suited me so I tried second one but then I run onto serious problems like:

  1. (A LOT of )repetative code - I created 4 classes for : int, double, long, Complex - my own type
  2. Creating multiple extension methods and so on.

Third one is just so unsafe that I rejected it immidietlay.

After my struggling I came to realise : 'Why don't I use RTTI and reflection?' I know, it is expensive in running time but why not use static constructor to do this?

Here is my idea (pseudocode):

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

I know that reflection is costly but it will do it only one time (per type)! And before you start argue : I am going to code T is int like this :

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

I am aware that I cannot explicitly convert T to int but there must be some sort of 'work around'. Problem is that (for example) Int32 has not explicit 'method' for operator+ hence, reflection is not of much use.

After all that introduction I have two questions :

  1. Is it a good approach or do you see major flaws in it?
  2. Is it doable? I don't want to start creating code without knowing for sure that my lambda function WILL work.

EDIT 1+2 I changed my code to generic. I figured that maybe you need an usage of my class, here you are :

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

ANSWER based on dasblinkenlight's answer I managed to do this :

 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;

            }
        }
}

And this is exactly what I was looking for.

MaLiN2223
  • 1,290
  • 3
  • 19
  • 40
  • Feel free to ask any questions if there is anything not clear. It is not a homework of some sort - just me having fun with C# but I really want to create good code. – MaLiN2223 Jan 20 '16 at 21:57
  • Are you doing this as an exercise or you just need Mathematical matrix operations for something else? – Eduardo Wada Jan 20 '16 at 21:57
  • It seems to me like you could get this kind of functionality from some library around – Eduardo Wada Jan 20 '16 at 22:01
  • I will probably create some LUP decompositions or calculating determinant in many ways or maybe even smith normal form but It always be only for fun not some serious calculations. – MaLiN2223 Jan 20 '16 at 22:01
  • @EduardoWada, I am trying to build one myself. – MaLiN2223 Jan 20 '16 at 22:14

5 Answers5

2

Yes, your approach will work fine.

Your static constructor will run for each type parameter T, ensuring that Add is set correctly.

You may want to separate out the addition logic into a separate class outside your matrix, and use that class to run operations based on type for your matrix. For example, if you also need multiplication, you could build a ITypeTraits<T> interface that has Add and Multiply:

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

Now you can build implementations of ITypeTraits<T> for individual types, e.g.

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

make a dictionary out of them

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

and get the one you need to perform operations:

ITypeTraits<T> traits = (ITypeTraits<T>)traitByType(typeof(T));
T first = ...
T second = ...
T sum = traits.Add(first, second);
T prod = traits.Mul(first, second);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I am very sorry, I forgot to add in my code! Will your answer be the same after this modification? – MaLiN2223 Jan 20 '16 at 22:05
  • @Malin No, it would not be the same. Here is the approach that I used successfully in a project where I needed to do something like yours. I used classes in place of lambdas, and avoided conditionals and casts. – Sergey Kalinichenko Jan 20 '16 at 22:24
  • I wrote a very similar approach, but without the dictionary. Check it below. – Vilx- Jan 20 '16 at 22:36
  • Thank you @dasblinkenlight, this idea is briliant! It was exactly was I was looking for. Also : check my updated question I posted my code based on yours. – MaLiN2223 Jan 21 '16 at 09:38
1

We can do this natively in C# 11 / .NET 7 (or above):

class Matrix<T> where T : INumber<T> // or just IAdditionOperators<T,T,T>
{
    T x, y, z; // just to show we can do things
    public T Sum() => x + y + z;
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

What is wrong with #3? You can just check for type, like so:

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;
    }
}
Joseph M. Shunia
  • 310
  • 2
  • 10
0

Bulding on dasblinkenlight's answer, here's my version of it. The benefit is that it doesn't need a dictionary lookup, instead making the type system do it. Should be faster, I think, but I haven't measured it. Also a bit less typing.

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);
    }
}
Community
  • 1
  • 1
Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • He mentions building a library, a library's user would be required to change MatrixBase's implementation in order to use this code on his custom class that contains the + operator – Eduardo Wada Jan 20 '16 at 22:45
  • @EduardoWada - Well, he can add an extension point where the user of the library can supply these functions. Also, if he can figure out a way to generate these functions, that too can be incorporated. – Vilx- Jan 20 '16 at 22:53
0

I think you are on the right path, in order to avoid using reflection, you are required to somehow inform the compiler that you know "T" has the "+" operator, however, this feature does not yet exist in C#, so this is impossible to implement without runtime type checking or imposing other constraints.

If you don't care about the performance, you could use dynamic:

(dynamic)first + (dynamic)second

but that will take several reflection performance hits in every operation

Or you could use some other more complex approach that caches the specific methods in a dictionary, but you won't escape calling at least .GetType() in your add's implementation

Community
  • 1
  • 1
Eduardo Wada
  • 2,606
  • 19
  • 31