Как исправить ошибку CS0029, вызванную дженериками в C#

#c# #generics

#c# #дженерики

Вопрос:

Я пытаюсь написать статический Parse метод в классе Matrix<T, V> , который отвечает за построение Matrix<T, Vector<T>> правильных размеров из строки (в этом классе матрицы поддерживаются векторами строк). Matrix<T, V> Существует несколько подклассов (например, Matrix2x<T> , Matrix2x2<T> и т.д.). Разные подклассы имеют разные размеры в дополнение к разным возможностям получения / установки. Этот код адаптируется из существующего функционального кода Java. Мы с другом экспериментируем с использованием C # вместо Java, с которой мы более знакомы.

Проблема в том, что эти параметризованные подклассы не могут быть преобразованы (либо неявно, либо с приведением) в базовый класс, параметризованный с помощью Vector<T> базового класса. Мне непонятно, почему следующий код не компилируется, хотя я полагаю, что это вызвано конкретностью дженериков C # по сравнению с дженериками Java.

Я подумал, возможно ли, что ошибка компиляции могла быть вызвана попыткой вернуть матрицу, параметризованную по типу Vector<T> , потому что она абстрактна. Изменение Vector<T> на конкретный класс (в комплекте с фиктивными методами) не решило проблему; ошибка компиляции сохранялась.

 public static Matrix<T, Vector<T>> Parse(Func<string, Scalar<T>> parser, string str, bool mutable = false) {
    MutableVector2D<T> mv2 = new MutableVector2D<T>();
    Vector2D<T> v2 = mv2;
    Vector<T> v = v2;

    MutableMatrix2x2<T> mm22 = new MutableMatrix2x2<T>();
    Matrix2x2<T, MutableVector2D<T>> m22 = mm22;
    Matrix2x2<T, Vector2D<T>> m22_ = m22; // Does not compile (compilation error CS0029)
    Matrix2x<T, Vector2D<T>> m2 = m22_;
    Matrix<T, Vector2D<T>> m = m2;
    Matrix<T, Vector<T>> answer = m; // Does not compile (compilation error CS0029)
    return answer;
}
  

Как показано во фрагменте кода, a MutableVector2D<T> может быть неявно преобразован в Vector2D<T> и в Vector<T> . Однако MutableMatrix2x2<T> может быть неявно преобразован в Matrix2x2<T, MutableVector2D<T>> , но не в Matrix2x2<T, Vector2D<T>> , что меня озадачивает. Приведение к Matrix2x2<T, Vector2D<T>> (которое не должно быть необходимым, насколько я понимаю проблему) ничего не решило.

Соответствующие заголовки классов перечислены ниже:

 public sealed class MutableVector2D<T> : Vector2D<T> {...}
public abstract class Vector2D<T> : Vector<T> {...}
public abstract class Vector<T> : ICopyable<Vector<T>>, IParser<Vector<T>>, IComparable<Vector<T>>, IEnumerable<Scalar<T>> {...}

public sealed class MutableMatrix2x2<T> : Matrix2x2<T, MutableVector2D<T>> {...}
public abstract class Matrix2x2<T, V> : Matrix2x<T, V> where V : Vector2D<T> {...}
public abstract class Matrix2x<T, V> : Matrix<T, V> where V : Vector2D<T> {...}
public abstract class Matrix<T, V> : ICopyable<Matrix<T, V>>, IParser<Matrix<T, Vector<T>>>, IComparable<Matrix<T, Vector<T>>>, IEnumerable<Scalar<T>> where V : Vector<T> {...}
  

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

 public static Matrix<T, Vector<T>> Parse(Func<string, Scalar<T>> parser, string str, bool mutable = false) {
    string[] lines = str.Split('n');
    int m = lines.Length;
    if (m == 0) {
        throw new ArgumentException("Cannot construct a Matrix with 0 rows.", "str");
    }
    string line = lines[0];
    int leftIndex = line.IndexOf('[')   1;
    string[] entries = line.Substring(leftIndex, line.IndexOf(']', leftIndex)).Split(',');
    int n = entries.Length;
    if (n < 2) {
        throw new ArgumentException("Cannot construct a Matrix with fewer than 2 columns.");
    }
    Scalar<T>[,] parsedEntries = new Scalar<T>[m, n];
    for (int j = 0; j < n; j  ) {
        parsedEntries[0, j] = parser.Invoke(entries[j]);
    }
    for (int i = 1; i < m; i  ) {
        line = lines[i];
        leftIndex = line.IndexOf('[')   1;
        entries = line.Substring(leftIndex, line.IndexOf(']', leftIndex)).Split(',');
        if (entries.Length != n) {
            throw new ArgumentException("Cannot construct a Matrix from a jagged array of entries", "str");
        }
        for (int j = 0; j < n; j  ) {
            parsedEntries[i, j] = parser.Invoke(entries[j]);
        }
    }
    switch (n) {
        case 2:
            if (m == n) {
                if (mutable) {
                    return new MutableMatrix2x2<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix2x2<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            } else {
                if (mutable) {
                    return new MutableMatrix2x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix2x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            }
        case 3:
            if (m == n) {
                if (mutable) {
                    return new MutableMatrix3x3<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix3x3<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            } else {
                if (mutable) {
                    return new MutableMatrix3x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix3x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            }
        case 4:
            if (m == n) {
                if (mutable) {
                    return new MutableMatrix4x4<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix4x4<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            } else {
                if (mutable) {
                    return new MutableMatrix4x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrix4x<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            }
        default:
            if (m == n) {
                if (mutable) {
                    return new MutableMatrixNxN<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrixNxN<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            } else {
                if (mutable) {
                    return new MutableMatrixNx<T>(parsedEntries); // Does not compile (compilation error CS0029)
                } else {
                    return new ImmutableMatrixNx<T>(parsedEntries); // Does not compile (compilation error CS0029)
                }
            }
    }
}
  

Заранее большое спасибо за любую помощь, которую вы можете мне оказать.

Комментарии:

1.Просто потому, что два типа имеют определенное отношение наследования, это не означает, что общий параметр, заданный для этих типов, демонстрирует одинаковое отношение. a MutableVector2D наследует от Vector2D , но, например, a List<MutableVector2D> не наследует от List<Vector2D> . У вас аналогичная проблема в вашем коде. Общего исправления нет, потому что в целом разрешать назначение небезопасно для типов, особенно в условиях изменчивости.

2. Смотрите Блог Эрика Липперта о различиях с дженериками. Посмотрите на использование интерфейсов.