#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 #.