Backdooring Generic Lists через IList - PullRequest
       28

Backdooring Generic Lists через IList

8 голосов
/ 12 ноября 2010

У меня есть сценарий, в котором класс загружает объекты одного типа, из-за абстракций я не могу использовать универсальный класс (генерики имеют тенденцию распространяться как рак :), но я часто хочу работать с универсальной версией объектов после извлечения, что привело к следующему коду (упрощенно):

List<SomeClass> items = Storage.LoadItems(filename).OfType<SomeClass>().ToList();

Где LoadItems возвращает список , тогда я понял, почему бы вместо этого не иметь

public void LoadItems(string filename,IList list);

Теперь я могу сделатьэто вместо этого

List<SomeClass> items = new  List<SomeClass>();
LoadItems(filename,items);

Что должно быть более эффективным.Это также кажется немного более гибким, так как я могу взять существующий список и добавить новые элементы.Итак, мои вопросы: это общий шаблон или у вас есть другой / лучший способ добиться этого?

Мне также немного любопытно, что вы можете сделать это, если вы попытаетесь добавить объектнеправильный тип вы получаете исключение, но означает ли это, что универсальные списки также делают проверку типа?(что кажется немного ненужным)

РЕДАКТИРОВАТЬ На самом деле может быть немного элегантнее изменить шаблон на

public IList LoadItems(string filename,IList list=null);

, чтобы вы могли свободно использовать оператори если список не передан, вы можете просто создать экземпляр List

Ответы [ 4 ]

3 голосов
/ 12 ноября 2010

List<T> реализует IList явно. Реализации приводят к T и вызывают обычные (обобщенные) методы.

Таким образом, проверка типов происходит, только если вы явно вызываете методы IList. Например:

void System.Collections.IList.Insert(int index, Object item) 
{
    ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); 

    try {
        Insert(index, (T) item); 
    }
    catch (InvalidCastException) {
        ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
    } 
}

В нем не используется ключевое слово as, поскольку T может быть типом значения.
Вы можете написать as T, только если написали where T : class.

1 голос
/ 12 ноября 2010

Использование IList нормально в большинстве случаев;и , безусловно, быстрее, чем использование отражения или dynamic для достижения того же.

Да, это добавит проверку типов (в силу приведения / отмены), но это не будетбыть обременительнымЕсли T - это struct, то у вас также есть бокс / распаковка, но это тоже не так плохо, как опасаются люди.

В этом сценарии IList мне подойдет.

0 голосов
/ 12 ноября 2010

Мне нравится второй подход.

Вы можете передать IList, но затем проверить тип, чтобы увидеть, является ли он универсальным списком, и если да, получить универсальный тип и загрузить только записи этого типа:

   Type itemType = typeof (object);
   if(list.GetType().GetGenericArguments().Length>0)
   {
       itemType = list.GetType().GetGenericArguments()[0];
   }

   for (int i = 0; i < recordCount; i++)
   {
      if(record.GetType().IsInstanceOfType(itemType))
   }
0 голосов
/ 12 ноября 2010

Ваше решение выглядит хорошо, в этом нет ничего плохого.

Что касается Add, оно на самом деле не выполняет проверку типа.Код для Add, на который вы ссылаетесь, таков:

int System.Collections.IList.Add(Object item)
{
    try {
        Add((T) item);
    }
    catch (InvalidCastException) {
        throw ...;
    }

    return Count - 1;
}

Он не выполняет проверку типа;это просто попытка / улов.

...