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.
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>
hereT 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:
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 :
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.
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);