I'm not sure if this is possible, but if it is then it would be useful.
I am attempting to program in a class called Matrix<T>
. The intent is to be able to have matrices of various data types, such as integers, floats, doubles, etc.
I now want to define addition:
public static Matrix<T> operator (Matrix<T> first, Matrix<T> second)
{
if (first.dimension != second.dimension)
{
throw new Exception("The matrices' dimensions do not match");
}
Matrix<T> add = new Matrix<T>(first.dimension);
for (int i = 1; i <= first.rows; i )
{
for (int j = 1; j <= first.columns; i )
{
add[i,j] = first[i,j] second[i,j];
}
}
return add;
}
There is an issue with the line add[i,j] = first[i,j] second[i,j];
since the operation
is not defined on a general object of type T
.
I only want to specify matrices where T
is a type such that addition is defined, however. So, I can make a matrix of int
s, float
s, double
s, etc. but if I were to try and define a matrix of, say, int[]
s, I would want this to throw an exception since
is not defined for int[]
s.
So, instead of writing T
, is there some way of telling the computer "this can take in any generic type, as long as an operator
is defined on the type? Or, is this not possible and I would have to sepeately define a matrix of int
s, matrix of float
s, and so on?
Edit: I don't see how the linked question from closure is related to this - I see nothing about operators there. If they are related, can somebody explain how?
CodePudding user response:
Currently it is not possible (at least without losing compile time safety or changing the API) but with preview features enabled and System.Runtime.Experimental
nuget you can use IAdditionOperators
to restrict T
to have
operator defined. I would say that adding this interface also to Matrix
itself can be a good idea:
class Matrix<T> : IAdditionOperators<Matrix<T>, Matrix<T>, Matrix<T>> where T : IAdditionOperators<T, T, T>
{
public static Matrix<T> operator (Matrix<T> left, Matrix<T> right)
{
// swap to real implementation here
T x = default;
T y = default;
Console.WriteLine(x y);
return default;
}
}
See also:
- Generic math (especially section about trying it out, note - VS 2022 recommended)
CodePudding user response:
It's possible using reflection
class Int
{
readonly int v;
public int Get => v;
public Int(int v)
{
this.v = v;
}
public static Int operator (Int me, Int other) => new Int(me.v other.v);
}
class Arr<T>
{
T[] _arr;
public Arr(T[] arr)
{
_arr = arr;
}
public T this[int index] => _arr[index];
public static Arr<T> operator (Arr<T> me, Arr<T> other)
{
var addMethod = typeof(T).GetMethod("op_Addition");
if (addMethod == null)
throw new InvalidOperationException($"Type {typeof(T)} doesn't implement ' ' operator");
var result = me._arr.Zip(other._arr)
.Select(elements => addMethod.Invoke(null, new object[] { elements.First, elements.Second }))
.Cast<T>()
.ToArray();
return new Arr<T>(result);
}
}
[Test]
public void TestAdd()
{
var firstArray = new Arr<Int>(new[] { new Int(1), new Int(2) });
var secondArray = new Arr<Int>(new[] { new Int(2), new Int(3) });
var sum = firstArray secondArray;
Assert.AreEqual(3, sum[0].Get);
Assert.AreEqual(5, sum[1].Get);
}
Reduced the example to array. Unfortunetly it compiles even if T doesn't implement add operator, so you will get a exception in runtime. You could also check if the add method has proper signature (returns T and takes two T's). If you need help understanding the code, let me know!