Как передать список классов в список интерфейса?

#c# #class #list #interface

#c# #класс #Список #интерфейс

Вопрос:

У меня есть такая функция:

 DoSomething(List<IMyInterface>)
  

IMyInterface — это интерфейс, а MyClass — класс, реализующий этот интерфейс
Класс MyClass:IMyInterface

Я звоню DoSomething(List<MyClass>) , и, похоже, это не работает. Как я могу передать список класса в список интерфейса класса в качестве параметра функции? Спасибо!

Ответ №1:

Если ваш код просто повторяет последовательность внутри метода (не добавляя, не удаляя или не получая доступ по индексу), измените свой метод на один из следующих

 DoSomething(IEnumerable<IMyInterface> sequence)
DoSomething<T>(IEnumerable<T> sequence) where T : IMyInterface
  

IEnumerable<> Интерфейс является ковариантным (начиная с .NET 4) (первый вариант). Или вы могли бы использовать последнюю подпись при использовании C # 3.

В противном случае, если вам нужны индексированные операции, преобразуйте список перед его передачей. При вызове у вас может быть

 // invocation using existing method signature 
DoSomething(yourList.Cast<IMyInterface>().ToList());

// or updating method signature to make it generic
DoSomething<T>(IList<T> list) where T : IMyInterface
  

Что последняя подпись позволит вам сделать, так это также поддерживать добавление или удаление в список (видимый на сайте вызова), а также позволит вам использовать список без его предварительного копирования.

Тем не менее, если все, что вы делаете, это перебираете список в цикле, я бы предпочел метод accept IEnumerable<> .

Комментарии:

1. Для индексированных операций и удаления тоже не DoSomething<T>(IList<T> list) where T : IMyInterface сработает? (Без необходимости копирования списка.)

2. @Sli, да, он мог бы сделать метод универсальным. Я обновил, чтобы показать общие параметры.

3. Альтернативой было бы определить интерфейс IReadableList<out T> , а затем написать класс-оболочку, который реализует как это, так и IList<T> . К сожалению, для использования класса-оболочки придется изменить другой код. Можно было бы определить расширяющееся преобразование из List<T> в ListWrapper<T> , но это было бы несколько неприятно, и это не помогло бы системе распознать, что List<Dog> можно превратить во что-то (ListWrapper<Dog> ), который реализует IReadableList<Animal> .

Ответ №2:

В целом это небезопасно, потому что списки изменчивы. Предположим, вы передаете кому-то ссылку на a List<MyClass> как a List<IMyInterface> , тогда они делают:

 void Foo(List<IMyInterface> list)
{
    IMyInterface x = new MyOtherClassWhichAlsoImplementsIMyInterface();
    list.Add(x);
}
  

Теперь ваш List<MyClass> содержит экземпляр класса, который не является a MyClass . Это нарушило бы безопасность типов. (Как отмечалось в других ответах, вы можете избежать этой проблемы, передав только IEnumerable<> интерфейс списка, который обеспечивает доступ только для чтения и поэтому безопасен).

Для получения более подробной информации см. Использование различий в интерфейсах для общих коллекций в MSDN. Смотрите также хорошее краткое изложение ковариации и контравариантности и различных функций C #, которые ее поддерживают.

Ответ №3:

Если вам нужно только просмотреть список, объявите метод с помощью IEnumerable . Если вы хотите добавить элементы в список, то то, что вы просите, не является типизированным и в результате может быть запрещено в C #.