Почему я не могу конвертировать из List<IScreen> в List<IRenderable>, даже если IScreen реализует IRenderable?

Довольно понятный вопрос, рад за любой ответ. Для меня это не имеет смысла. Меня не волнует потеря информации. Я просто хочу объединить все мои списки, которые как-то наследуют IRenderable, в один список, а затем вызвать для него методы IRenderable. У меня такое ощущение, что это какое-то основное правило программирования, которое я забыл...

редактировать: это код:

interface IRenderable
{
    void Render();
}
interface IScreen : IRenderable
{
}
public List<IScreen> Screens = new List<IScreen>();
List<IRenderable> renderList = new List<IRenderable>();
renderList = ((List<IRenderable>)Screens; // error: cannot convert type IScreen to IRenderable

edit2: кстати, у меня установлен.net 4.0, но ссылка все еще остается System.dll версии 2.0.5.0

3 ответа

Решение

Вы не даете много деталей. Но давайте предположим, что у вас есть:

IRenderable {}

IScreen : IRenderable {}

private List<IRenderable> ConcatLists(IEnumerable<IRenderable> first, IEnumerable<IRenderable> second) { ... }

Теперь вы не сможете напрямую вызывать метод ConcatLists с помощью IEnumerableв версиях C# до C#4. Причина этого называется со-и контравариантностью. В Framework 4 IEnumerable объявлен ковариантным, что означает, что он может быть неявно преобразован в IEnumerable, где T имеет меньший тип (IRenderable в вашем случае).

Если вы хотите вызвать такой метод в C#3, вы можете использовать метод расширения, чтобы помочь вам.

Это может быть:

ConcatLists(first.Cast<IRenderable>(), second);

Если "first" объявлено как тип IRenderable.

Я думаю, вы действительно пытаетесь конвертировать из List<IScreen> в List<IRenderable> или что-то подобное.

Вы не можете сделать это вообще до.NET 4 из-за общей неизменности. В.NET 4 вы можете сделать это для интерфейсов и делегатов, где это необходимо. В частности, вы можете конвертировать из IEnumerable<IScreen> в IEnumerable<IRenderable>, Если вы просто пытаетесь объединить список, это должно быть хорошо - вы должны иметь возможность использовать List<T>.AddRange с каждым из ваших списков... но опять же, это применимо только в C# 4. Другой вариант в C# 4 будет что-то вроде:

var combined = firstList.Concat<IRenderable>(secondList).ToList();

В качестве примера того, почему это не всегда безопасно (разрешенные варианты использования в C# 4 безопасны), рассмотрим следующий код:

List<Apple> apples = new List<Apple> { new Apple(...) };
List<Fruit> fruit = apples;
fruit.Add(new Orange());

Теперь список, который apples относится к апельсину! Это явно нехорошо с точки зрения безопасности типов.

Как говорит Дрис, если вы можете дать больше информации (например, что у вас есть и чего вы хотите), мы сможем помочь вам больше.

List<T0> не поддается List<T1>, Вы должны сделать кастинг для каждого элемента списка. Расширение Cast для IEnumarable состоит в следующем:

List<IScreen> Screens = new List<IScreen>();
List<IRenderable> renderList = Screens.Cast<IRenderable>().ToList();
Другие вопросы по тегам