Generics and casting - cannot cast inherited class to base class

I know this is old, yet I am still not very good with understanding those problems. Can anyone tell me why the following does not work (throws a runtime exception about casting)?

public abstract class EntityBase { }
public class MyEntity : EntityBase { }

public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }

And now the casting line:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;

So, can anyone explain how is this invalid? And, I you are not in the mood to explain - is there a line of code I can use to actually do this cast?

26
задан Jefim 20 August 2010 в 07:06
поделиться

3 ответа

RepositoryBase является не базовым классом MyEntityRepository. Вы ищете общую дисперсию, которая существует в C# в ограниченной степени, но здесь не применима.

Предположим, что ваш класс RepositoryBase имеет такой метод:

void Add(T entity) { ... }

Теперь подумайте:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 
baseRepo.Add(new OtherEntity(...));

Теперь вы добавили сущность другого типа в MyEntityRepository... и это не может быть правильным.

В принципе, родовая дисперсия безопасна только в определенных ситуациях. В частности, общая ковариация (то, что вы здесь описываете) безопасна только тогда, когда вы получаете значения только "из" API; общая контравариация (которая работает наоборот) безопасна только тогда, когда вы вводите значения только "в" API (например, общее сравнение, которое может сравнивать любые две фигуры по площади, можно рассматривать как сравнение квадратов).

В C# 4 это доступно для общих интерфейсов и общих делегатов, а не для классов - и только для ссылочных типов. Дополнительную информацию смотрите в MSDN, прочтите C# in Depth, 2nd edition, chapter 13 или серию блогов Эрика Липперта на эту тему. Кроме того, я выступил с часовым докладом на эту тему на NDC в июле 2010 года - видео доступно здесь.

31
ответ дан 28 November 2019 в 06:44
поделиться

Для этого требуется ковариация или контравариантность, поддержка которых ограничена в .Net и не может использоваться в абстрактных классах. Однако вы можете использовать дисперсию в интерфейсах, поэтому возможное решение вашей проблемы - создать IRepository, который вы используете вместо абстрактного класса.

    public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items.
    }
    public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase {
    }
    public class MyEntityRepository : RepositoryBase<MyEntity> {
    }

    ...

    IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;
10
ответ дан 28 November 2019 в 06:44
поделиться

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

Давайте заменим MyEntityRepository на MyStringList , например:

class MyStringList : List<string> { }

Теперь, похоже, вы хотите, чтобы MyEntityRepository можно было преобразовать в RepositoryBase , поскольку это должно быть возможно, поскольку MyEntity происходит от EntityBase .

Но строка происходит от объекта , не так ли? Таким образом, с помощью этой логики мы должны иметь возможность преобразовать MyStringList в List .

Давайте посмотрим, что может случиться, если мы позволим это ...

var strings = new MyStringList();
strings.Add("Hello");
strings.Add("Goodbye");

var objects = (List<object>)strings;
objects.Add(new Random());

foreach (string s in strings)
{
    Console.WriteLine("Length of string: {0}", s.Length);
}

Ой-ой. Внезапно мы перебираем список List и находим объект Random . Это не хорошо.

Надеюсь, это облегчит понимание проблемы.

18
ответ дан 28 November 2019 в 06:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: