C # foreach на коллекции интерфейса - PullRequest
1 голос
/ 29 сентября 2011

Мне интересно, есть ли какая-нибудь функциональность, встроенная в C # / LINQ для упрощения следующего:

foreach(var item in collection)
{
    if (item.GetType() == typeof(Type1)
         DoType1(item as Type1);
    else if (item.GetType() == typeof(Type2))
         DoType2(item as Type2);
    ...
}

к чему-то вроде:

collection.ForEachType(Type1 item => DoType1(item), Type2 item => DoType2(item));

Я понимаю, что следующее близко:

collection.OfType<Type1>.ToList().Foreach(item => DoType1(item));
collection.OfType<Type2>.ToList().Foreach(item => DoType2(item));

Но это не работает, когда код зависит от порядка коллекции.

Ответы [ 6 ]

6 голосов
/ 29 сентября 2011

Первое, на что я посмотрю, это полиморфизм; вместо этого я могу использовать виртуальный метод, и item.DoSomething()?

Следующая вещь, на которую я бы посмотрел, была бы дискриминацией по перечислению, т.е.

switch(item.ItemType) {
    case ItemType.Foo: ...
    case ItemType.Bar: ...
}

(и добавить дискриминатор к общему интерфейсу / базовому классу)

Если типы могут быть что угодно , то у 4.0 есть хитрость; если вы вызываете te метод одинаково для каждой перегрузки, вы можете заставить dynamic беспокоиться о его выборе:

dynamic x = item;
DoSomething(x);
5 голосов
/ 29 сентября 2011

Нет ничего встроенного в LINQ, нет. Я бы предостерег вас от использования GetType(), как это, хотя - обычно более целесообразно использовать is или as с последующей пустой проверкой:

foreach(var item in collection)
{
    Type1 itemType1 = item as Type1;
    if (itemType1 != null)
    {
         DoType1(itemType1);
         continue;
    }
    Type2 itemType2 = item as Type1;
    if (itemType2 != null)
    {
         DoType2(itemType1);
         continue;
    }
    // etc
}

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

Этот тип тестирования типов обычно нахмурился, имейте в виду - как правило, лучше поместить поведение в сам тип как виртуальный метод и вызывать его полиморфно.

1 голос
/ 29 сентября 2011

Ваш пробег может отличаться.

Dictionary<Type, Action<object>> typeMap = new Dictionary<Type, Action<object>>();
typeMap[typeof(Type1)] = item => DoType1(item as Type1);
typeMap[typeof(Type2)] = item => DoType2(item as Type2);

var typeToActionQuery =
  from item in source
  let type = item.GetType()
  where typeMap.ContainsKey(type)
  select new
  {
    input = item;
    method = typeMap[type]
  };

foreach(var x in typeToActionQuery)
{
  x.method(x.input);
}

Вот версия соответствующего запроса, которая учитывает производные типы (Обратите внимание, что элемент может соответствовать более чем 1 типу и поэтому обрабатываться несколько раз).

var typeToActionQuery =
  from item in source
  from kvp in typeMap
  where kvp.Key.IsInstanceOfType(item)
  select new
  {
    input = item;
    method = kvp.Value
  };
1 голос
/ 29 сентября 2011

Как насчет чего-то вроде:

var typeActions = new Dictionary<Type,Action<Object>>();
typeActions.Add(typeof(Type1), obj => DoType1((Type1)obj));
typeActions.Add(typeof(Type2), obj => DoType2((Type2)obj));

collection.Foreach(obj => typeActions[obj.GetType()](obj));

Этот код не проверен (набран прямо в браузере).

0 голосов
/ 29 сентября 2011

Мне кажется, что если вы просто замените "item.GetType () == typeof (Type1)" на "item is Type1", ваш цикл foreach будет достаточно простым.

0 голосов
/ 29 сентября 2011

Не по умолчанию.Попробуйте Reactive Extensions или Elevate

Reactive Extensions и Elevate содержат реализацию ForEach.Оба имеют несколько методов, расширяющих функциональность linq.

Вы не найдете ForEachType, но ForEach (Rx или Elevate) и OfType <> (Linq) дадут вам то, что вы хотите.*

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...