#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
, но, например, aList<MutableVector2D>
не наследует отList<Vector2D>
. У вас аналогичная проблема в вашем коде. Общего исправления нет, потому что в целом разрешать назначение небезопасно для типов, особенно в условиях изменчивости.2. Смотрите Блог Эрика Липперта о различиях с дженериками. Посмотрите на использование интерфейсов.