C # .NET передает коллекцию объектов InterfaceImplementingClass подпрограмме, которая принимает коллекцию объектов Interface - PullRequest
5 голосов
/ 25 февраля 2009

У меня есть рутина

public void SomeRoutine(List<IFormattable> list) { ... }

Затем я пытаюсь вызвать эту процедуру

List<Guid>list = new List<Guid>();
list.Add(Guid.NewGuid());
SomeRoutine(list);

И он завершается с ошибкой во время компиляции. System.Guid реализует IFormattable, но я получаю ошибку

не может конвертировать из 'System.Collections.Generic.List' в 'System.Collections.Generic.List'

ПРИМЕЧАНИЕ: вы получите ту же ошибку, если вы просто используете массив Guids. Дженерики это НЕ причина ....

Но! Учитывая это

public void SomeRoutine2(IFormattable obj) { ... }

и это

Guid a = Guid.NewGuid();
SomeRoutine2(a);

Это компилируется! Вопрос в том, ПОЧЕМУ? Почему я могу передать объект Guid (который реализует IFormattable) в подпрограмму, которая принимает объект IFormattable, но когда я пытаюсь развернуть его в коллекцию (общий список, или массив, или что-нибудь еще), я получаю ошибка преобразования?

У меня было чертовски много времени, чтобы найти ответ, и я подумал, что это будет лучшее место, чтобы пойти.

Ответы [ 4 ]

8 голосов
/ 25 февраля 2009
5 голосов
/ 25 февраля 2009

Это классический вариант использования дженериков; попробовать:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable
{ ... }

Теперь внутри SomeRoutine у вас есть доступ ко всем IFormattable членам, но он будет работать с:

List<Guid>list; ...
SomeRoutine(list); // note no need to specify T

Редактировать: я также написал о различиях между ковариацией 4.0 и обобщениями для этого сценария.

3 голосов
/ 25 февраля 2009

Проблема в том, что List - это не только коллекция, из которой вы можете прочитать некоторые IFormattables, это также коллекция, в которую вы можете добавить IFormattables. Список соответствует первому требованию, но не второму. Что если SomeRoutine был

public void SomeRoutine(List<IFormattable> list)
{
    list.Add(5);
}

Это Int32 является IFormattable, поэтому метод должен иметь возможность добавить его в список , который он запрашивал. Это не сработает, если компилятор позволит вам передать ваш список .

Новые функции C # 4.0, на которые ссылается BFree, позволят вам сообщить компилятору, когда все это безопасно. Вы можете сказать либо

  • Хотя я попросил ссылку IFormattable, я буду только читать ее. Если это действительно ссылка на Guid, то я могу смело обращаться с ней как с IFormattable, и я не буду пытаться присвоить ему Int32.
  • Хотя я попросил ссылку IFormattable, я буду писать только на нее. Если это действительно ссылка на объект, то я могу смело присвоить ей свой IFormattable, и не имеет значения, является ли текущее значение IFormattable, потому что я не буду его читать.
2 голосов
/ 25 февраля 2009

Вы пытались объявить list объектом List<IFormattable> вместо List<Guid>? Это должно избавить вас от ошибки компиляции.

...