2017-01-22 18 views
5

Предположит, у нас есть идея для общего класса Matrix<T> где T является числовым типом (Complex или double или float, int и т.д.).неявного преобразования для общего класса ограничен только к некоторым типам

Естественно, что у нас есть неявное преобразование в C# из float в double, от double к Complex. Общее правило заключается в том, что мы имеем неявное преобразование из меньшего типа в более крупный. Все идет нормально.

Теперь представьте, что мы реализуем наш тип Matrix<T>. Поскольку этот новый тип также является числовым (или, по крайней мере, он содержит числовые значения), вполне естественно иметь неявные преобразования от Matrix<float> до Matrix<double>, от Matrix<double> до Matrix<Complex> и т. Д. По крайней мере, это полезно иметь для математических операций, таких как: умножать, добавлять и т. д. Однако это кажется невозможным для правильной реализации, потому что неявный оператор требует, чтобы по крайней мере один тип был таким же, как класс, который мы его внедряем.

Пример: ниже код не компилируется, даже если он решил разрешить мои проблема.

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

Это не будет компилироваться, так как «CS0556 определяемого пользователем преобразование должно преобразовать в или из типа ограждающего» и, скажем, я в порядке с этим, потому что она является частью спецификации языка, но не должна быть там любой другой способ добиться этого?

Например, это тоже не скомпилировано.

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>.

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

Если кто-то заинтересован в фоновом режиме вокруг этой проблемы, вы можете взглянуть на https://github.com/mathnet/mathnet-numerics/issues/304

Другой вариант, чтобы решить эту проблему можно было бы использовать что-то вроде «операторов продолжения» (по аналогии с методами расширения), но те не присутствуют в C#.

+0

Честно говоря, как пользователь такого API, я бы боялся неявного подобного характера. Что относительно явных приемов? Будет ли это работать на вас? –

+0

@OndrejTucny Нет, к сожалению, я думаю, что неявный бросок - единственный способ пойти. –

ответ

0

Прежде всего, я не уверен, что использование дженериков с числовыми примитивными типами - хороший путь, чтобы спуститься. Это довольно серьезный недостающий аспект языка, и, по-видимому, нет никакого плана в его решении в кратчайшие сроки. Читайте this SO answer для получения дополнительной информации.

  1. Нет числового ограничения, лучшее, что вы можете сделать, это struct, что довольно плохо.
  2. Для любой арифметической поддержки, который является обязательным, если вы реализуете матрицу, необходимо определить интерфейс IArithmetic с Add, Multiply и т.д., и вы будете боксом и распаковкой повсюду, который может быть большое влияние на производительность.

    Ваш код намного хуже этого; потому что вам, похоже, не хватает общего интерфейса для T, вам нужно наложить на объект, чтобы сделать общий листинг успешным.

    Также, если у вас нет общего кода интерфейса, аналогичного if (typeof(T) == typeof(Complex)) ..., который является большим красным флагом при использовании дженериков; общий класс/метод должен работать на бесконечных типах типов, а не только нескольких предустановленных типов, то есть generic означает.

Я думаю, вы должны сделать шаг назад и переосмыслить свой подход. Когда система языкового типа, кажется, борется против вас и никоим образом не помогает, это верный признак того, что вы делаете что-то неправильно.

Почему бы вам просто не реализовать универсальную матрицу самого большого типа? Матрица комплексных чисел. В чем преимущество наличия матрицы поплавков или парных? Это не может быть производительность или эффективность памяти, потому что любое не общее решение будет лучше, чем ваш нынешний подход, при этом все бокс распаковывается.

UPDATE: После просмотра через библиотеку вы обосновывая это, я не знаю, почему вы не используете Matrix<T> по назначению в первую очередь: в качестве базового типа.

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

Где FloatMatrix очевидно FloatMatrix: Matrix<Float>.

+0

Ну, в первую очередь, я не реализую это с нуля. Это fork библиотеки MathNet.Numerics (я связан с проблемой, которую я пытаюсь решить). Честно говоря, библиотека имеет неплохой код, но мне действительно нужно это неявное преобразование для работы. AFAIK разрешает поплавки и т. Д. По соображениям производительности (работа с родными поставщиками, такими как Intel MKL). –

+0

@PawelTroka Ну, во-первых, это релевантная информация, которую вы должны задать перед собой в своем вопросе.Во-вторых, код в вашем приведении по-прежнему остается вашим, а приведение к 'object' просто для компиляции кода ужасно с точки зрения производительности. – InBetween

+0

@PawelTroka см. Редактирование моего ответа – InBetween

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 класс, определенный, который operator + определен вы можете объявить Matrix<Complex64> и сделать алгебру как var C = A+2*B непосредственно. Время выполнения вызовет вызов соответствующего оператора для встроенных типов или настраиваемых типов. Удар скорости минимален, потому что отражение для поиска подходящего метода для вызова производится только один раз за тип при первом использовании.

+0

Да, это похоже на интересный подход, но обратите внимание, что я уже открываю уже существующую библиотеку с большинством уже реализованных операторов. Таким образом, единственный способ для меня реализовать поведение, которое мне нужно, - это печально через неявные преобразования. –