Implicit conversion for a generic class is limited to only certain types

Suppose we have an idea for the general class Matrix<T> , where T is a numeric type ( Complex or double or float , int , etc.).

Naturally, we have implicit conversions in C # from float to double , from double to Complex . The general rule is that we have an implicit conversion from a smaller type to a larger one. So far so good.

Now imagine that we are implementing our type Matrix<T> . Since this new type is also in some sense numerical (or at least it contains numerical values), it is natural to have implicit conversions from Matrix<float> to Matrix<double> , from Matrix<double> to Matrix<Complex> and t .d. At the very least, it is nice to have them for mathematical operations such as multiplication, addition, etc. However, this seems impossible for proper implementation because the implicit operator requires that at least one type be the same as the class in which we implement it.

Example: the code below does not compile even though it may solve my problem.

 public abstract partial class Matrix<T> { /// <summary> /// Implicitly converts a matrix to double precision complex numbers. /// </summary> public static implicit operator Matrix<Complex64>(Matrix<double> matrix) { matrix.ToComplex(); } /// <summary> /// Implicitly converts a matrix to double precision real numbers. /// </summary> public static implicit operator Matrix<double>(Matrix<float> matrix) { matrix.ToDouble(); } } 

It will not compile because "CS0556 user-defined conversion must convert to or from the closing type" and let it say that I am fine with it because it is part of the language specification, but there should not be another way to achieve this?

For example, this also does not compile.

 public abstract partial class Matrix<double> { /// <summary> /// Implicitly converts a matrix to single precision real numbers. /// </summary> public static implicit operator Matrix<double>(Matrix<float> matrix) { matrix.ToDouble(); } } 

Is there a way to achieve this, it feels natural, so I think it should be achievable?

At the moment, I have created a workaround solution that allows implicit conversion for all types to the largest, but does not allow conversion from Matrix<float> to Matrix<double> , it only allows Matrix<Complex> conversions.

 public abstract partial class Matrix<T> { /// <summary> /// Implicitly converts a matrix to double precision complex numbers. /// </summary> public static implicit operator Matrix<Complex64>(Matrix<T> matrix) { return matrix.Map(x => { if (x is Numerics.Complex32) { var xc32 = (Numerics.Complex32)(object)x; return new Complex64(xc32.Real, xc32.Imaginary); } return new Complex64(Convert.ToDouble(x), 0); }, Zeros.AllowSkip); } } 

If anyone is interested in the background around this issue, you can take a look at https://github.com/mathnet/mathnet-numerics/issues/304

Another solution to this could be to use something like “extension operators” (similar to extension methods), but they are not present in C #.

+5
source share
2 answers

First of all, I'm not sure that using generics with numeric primitive types is a good way to go down. This is a pretty serious missing aspect of the language, and there seems to be no plan to solve it as soon as possible. Read this SO answer for more info.

  • There is no numerical limit, the best you can do is struct , which is pretty bad.
  • For any arithmetic support that is required if you are implementing a matrix, you need to define an IArithmetic interface with Add , Multiply , etc., and you will box and unpack everywhere, which can have a big impact on performance.

    Your code is much worse than that; because you do not seem to have a common interface for T , you need to expose the object in order to succeed in the gene.

    Also, if you don’t have a common interface code like if (typeof(T) == typeof(Complex)) ... , it appears, which is a big red icon when using generics; a common class / method should work with an infinite number of types, and not just with a few predefined types, which is a common tool.

I think you should take a step back and rethink your approach. When a linguistic type system seems to be fighting against you and does not help in any way, its a sure sign that you are doing something wrong.

Why don't you just implement a non-generic matrix of the largest type? Matrix of complex numbers. What is the advantage of having a matrix of floats or paired? It cannot be performance or memory efficiency, because any non-general solution will be better than your current approach, while all the box will be unpacked.

UPDATE: Having looked at the library where you use it, I'm not sure why you are not using Matrix<T> , as intended, in the first place: as the base type.

 public class DoubleMatrix : Matrix<double> { //now this is legal public static implicit operator DoubleMatrix(FloatMatrix matrix) => matrix.ToDouble(); } 

Where FloatMatrix is obviously FloatMatrix: Matrix<Float> .

0
source

The only way I know to create a generic vector or matrix class and make it do algebra (addition, subtraction, multiplication) is to emit MSIL codes that invoke operators (e.g. operator + ). It can then be used with any type that has static MyType operator + (MyType a, MyTYpe b) , as well as built-in types.

See this my answer for more details.

Using static methods from Operation<T> , you can have the following code example

 public class Matrix<T> { T[,] elements; static readonly Func<T,T> add = Operation<T>.Add; public static Matrix<T> operator + (Matrix<T> A, Matrix<T> B) { Matrix<T> result = new Matrix<T>(rows,cols); for(int i=0; i<rows; i++) { for(int j=0; j<cols; j++) { result[i,j] = add(A[i,j], B[i,j]); } } return result; } // rest of algebra } 

So, if you have a specific Complex64 class that has operator + , you can declare Matrix<Complex64> and make the same algebra as var C = A+2*B Runtime will invoke the appropriate statement for built-in types or custom types. The effect of speed is minimal, since the reflection to search for the appropriate method to call is performed only once for each type during the first use.

0
source

Source: https://habr.com/ru/post/1263175/


All Articles