Generics и casting – не могут передавать унаследованный class в базовый class
Я знаю, что это старо, но я все еще не очень хорошо разбираюсь в этих проблемах. Может ли кто-нибудь сказать мне, почему следующее не работает (выбрасывает исключение во runtime
кастинге)?
public abstract class EntityBase { } public class MyEntity : EntityBase { } public abstract class RepositoryBase where T : EntityBase { } public class MyEntityRepository : RepositoryBase { }
И теперь линия литья:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase baseRepo = (RepositoryBase)myEntityRepo;
Итак, может ли кто-нибудь объяснить, как это недействительно? И, я не в настроении объяснять – есть ли строка кода, которую я могу использовать для этого?
- Общие методы Java в classах дженериков
- Список OrderBy Алфавитный порядок
- Что заставляет javac выдавать предупреждение «использует непроверенные или небезопасные операции»
- Ошибка создания общего массива
- Как удалить элементы из общего списка во время итерации по нему?
- По какой причине я не могу создавать общие типы массивов в Java?
- Создать экземпляр родового типа в Java?
- Java Generics Подстановочные знаки с несколькими classами
- Доступ к свойствам через параметр Generic type
- Func с параметром out
- Преобразовать общий список / Перечислить в DataTable?
- Разница между списком, списком , Списком , списком и списком
- Java Generic List <Список >
RepositoryBase
не является базовым classом MyEntityRepository
. Вы ищете обобщенную дисперсию, которая существует в C # в ограниченной степени, но не применима здесь.
Предположим, что у вашего classа RepositoryBase
был такой метод:
void Add(T entity) { ... }
Теперь рассмотрим:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase baseRepo = (RepositoryBase )myEntityRepo; baseRepo.Add(new OtherEntity(...));
Теперь вы добавили другой тип сущности в MyEntityRepository
… и это не может быть прав.
В принципе, типичная дисперсия является безопасной только в определенных ситуациях. В частности, общая ковариация (именно то, что вы здесь описываете) безопасна только тогда, когда вы только когда-либо получаете значения «API»; общая контравариантность (которая работает наоборот) безопасна только тогда, когда вы когда-либо добавляли значения «в» API (например, общее сравнение, которое может сравнивать любые две формы по площади, можно рассматривать как сравнение квадратов).
В C # 4 это доступно для общих интерфейсов и общих делегатов, а не для classов – и только для ссылочных типов. Дополнительную информацию см. В MSDN , прочтите
Всякий раз, когда кто-то задает этот вопрос, я пытаюсь взять их пример и перевести его на что-то, используя более известные classы, которые явно незаконны (это то, что Джон Скит сделал в своем ответе , но я делаю это еще дальше, выполняя этот перевод).
Давайте заменим MyEntityRepository
на MyStringList
, например:
class MyStringList : List { }
Теперь вам, похоже, нужно, чтобы MyEntityRepository
был MyEntityRepository
к RepositoryBase
, аргументация заключается в том, что это должно быть возможным, поскольку MyEntity
происходит от EntityBase
.
Но string
получается из object
, не так ли? Таким образом, по этой логике мы должны иметь возможность отображать MyStringList
в List
.
Давайте посмотрим, что может случиться, если мы разрешим это …
var strings = new MyStringList(); strings.Add("Hello"); strings.Add("Goodbye"); var objects = (List
Ой-ой. Внезапно мы перечисляем List
и мы сталкиваемся с объектом Random
. Это не хорошо.
Надеюсь, это затруднит понимание проблемы.
Это требует ковариации или контравариантности, поддержка которых ограничена в .Net и не может использоваться в абстрактных classах. Вы можете использовать дисперсию на интерфейсах, так что возможным решением вашей проблемы является создание IRepository, который вы используете вместо абстрактного classа.
public interface IRepository where T : EntityBase { //or "in" depending on the items. } public abstract class RepositoryBase : IRepository where T : EntityBase { } public class MyEntityRepository : RepositoryBase { } ... IRepository baseRepo = (IRepository )myEntityRepo;