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; 

Итак, может ли кто-нибудь объяснить, как это недействительно? И, я не в настроении объяснять – есть ли строка кода, которую я могу использовать для этого?

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 , прочтите read C # in Depth, 2nd edition , Chapter 13 или блог блога Эрика Липперта по этой теме. Кроме того, я рассказал об этом в NDC в июле 2010 года – видео доступно здесь .

Всякий раз, когда кто-то задает этот вопрос, я пытаюсь взять их пример и перевести его на что-то, используя более известные 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)strings; objects.Add(new Random()); foreach (string s in strings) { Console.WriteLine("Length of string: {0}", s.Length); } 

Ой-ой. Внезапно мы перечисляем 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; 
  • Повторно используемый шаблон для преобразования события в задачу
  • В чем разница между необработанными типами, неограниченными дикими картами и использованием Object в generics
  • Вычисление неполного типа
  • Почему Func неоднозначен с Func <IEnumerable >?
  • Когда использовать общие методы и когда использовать wild-card?
  • Общее ограничение для сопоставления числовых типов
  • Объяснение обобщенного <T extends Comparable > в коллекции.sort / сопоставимый код?
  • Collections.emptyList () возвращает List ?
  • Как использовать reflection для вызова общего метода?
  • Что означает (угловые скобки) в Java?
  • Где общие типы хранятся в файлах java-classа?
  • Давайте будем гением компьютера.