泛型类的隐式转换仅限于某些类型

时间:2017-01-22 19:22:07

标签: c# .net generics implicit-conversion

假设我们知道通用类Matrix<T>,其中T是数字类型(Complexdoublefloatint等)

我们在C#中从floatdouble,从doubleComplex进行隐式转换是很自然的。一般规则是我们有从较小类型到较大类型的隐式转换。到目前为止一切都很好。

现在假设我们正在实施Matrix<T>类型。由于这种新类型在某种程度上也是数字(或者至少它包含数值),因此从Matrix<float>Matrix<double>进行隐式转换是很自然的,从Matrix<double>到{{1至少将它们用于数学运算,如乘法,加法等是很好的。但是这似乎不可能正确实现,因为隐式运算符要求至少一个类型与我们实现它的类相同。

示例:下面的代码无法编译,甚至认为它可以解决我的问题。

Matrix<Complex>

它不会编译,因为“CS0556用户定义的转换必须转换为封闭类型或从封闭类型转换”,让我说我很好,因为它是语言规范的一部分,但不应该有任何其他方式实现这个目标?

例如,这不会编译。

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

有没有办法实现这个目标,感觉很自然,所以我认为它应该是可以实现的?

目前我已经创建了一种解决方法,可以将所有类型的隐式转换转换为最大类型,但不能解决从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(); } } Matrix<float>的转换,它只会将转换解析为Matrix<double>

Matrix<Complex>

如果有人对此问题的背景感兴趣,您可以查看https://github.com/mathnet/mathnet-numerics/issues/304

解决此问题的另一个选择可能是使用“扩展运算符”(类似于扩展方法),但C#中不存在这些选项。

2 个答案:

答案 0 :(得分:0)

首先,我不确定使用带有数字基元类型的泛型是一条很好的道路。这是一个相当严重缺乏语言的方面,并且似乎没有任何计划在短时间内解决它。请阅读this SO answer了解详情。

  1. 没有数字约束,你可以做的最好的是struct,这非常糟糕。
  2. 对于任何算术支持,如果您要实现矩阵,则必须使用IArithmeticAdd等来定义Multiply接口。 #39;将在整个地方进行装箱和拆箱,这可能会对性能产生重大影响。

    你的代码远不如此;因为你似乎缺少T的通用接口,你需要转换为对象以使通用转换成功。

    此外,如果您没有开始出现类似于if (typeof(T) == typeof(Complex)) ...的通用界面代码,那么在使用泛型时这是一个很大的危险信号;泛型类/方法应该在无限种类型上工作,而不仅仅是几种预设类型,这就是泛型的含义。

  3. 我认为你应该退后一步,重新思考你的方法。当语言类型系统似乎在与你作斗争并且没有任何帮助时,这肯定表明你做错了。

    为什么不简单地实现最大类型的非通用矩阵?复数矩阵。拥有浮动或双打矩阵有什么好处?它不具备性能或内存效率,因为任何非通用解决方案都会比您当前的方法更好,所有装箱都会进行拆箱。

    更新:浏览完您所依据的库时,我不确定您为什么不首先使用Matrix<T>作为基础类型。

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

    FloatMatrix显然是FloatMatrix: Matrix<Float>

答案 1 :(得分:0)

我知道创建一个通用向量或矩阵类并让它进行代数(加法,减法,乘法)的唯一方法是发出调用运算符的MSIL代码(例如operator +) 。然后它可以与定义了static MyType operator + (MyType a, MyTYpe b)的任何类型以及内置类型一起使用。

有关详细信息,请参阅this answer of mine类似问题。

使用Operation<T>中的静态方法,您可以使用以下示例代码

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
}

因此,如果您确定了定义了Complex64的{​​{1}}类,则可以声明operator +并直接执行Matrix<Complex64>之类的代数。运行时将为内置类型或自定义类型调用适当的运算符。速度影响很小,因为找到适当的调用方法的反射只在首次使用时按类型完成一次