2013-02-26 3 views
3

У меня есть список объектов, и я должен написать метод, который извлекает некоторые элементы в соответствии с входным параметром, который является массивом типа. Функция должна возвращать массив элементов из списка, каждый из которых является экземпляром одного элемента из массива типов. В то же время эти элементы должны быть удалены из списка контейнеров, но только если они ВСЕ существуют в списке. Сравнение типов достигается методом Type.IsInstanceOfType(o).Извлечение элементов списка конкретных типов с LINQ

class A {} 
class B : A {} 
class C : A {} 
class D : A {} 

public static A[] ExtractElements (List<A> list, Type[] specifiers) {...} 

Type[] specifiers1 = new Type[2] {typeof(D), typeof(B)}; 
Type[] specifiers2 = new Type[3] {typeof(C), typeof(A), typeof(D)}; 
Type[] specifiers3 = new Type[2] {typeof(A), typeof(A)}; 
Type[] specifiers4 = new Type[2] {typeof(C), typeof(C)}; 

List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result1 = ExtractElements (list, specifiers1); 

list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result2 = ExtractElements (list, specifiers2); 

list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result3 = ExtractElements (list, specifiers3); 

list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result4 = ExtractElements (list, specifiers4); 

Результаты этого фрагмента кода будет:

result1 is {D, B}, list is {A, C, A} 
    result2 is {C, B, D}, list is {A, A} 
    result3 is {B, A}, list is {D, C, A} 
    result4 is empty array, list is {B, A, D, C, A} 

В отдельном усилии было бы возможно, чтобы написать подобный метод ExtractElements который только возвращает непустой массив, если список содержит пункты запрошенный виды, порядок в списке соответствует порядку элементов в массиве типов ввода, как следующие

Type[] specifiers5 = new Type[2] {typeof(B), typeof(D)}; 
Type[] specifiers6 = new Type[2] {typeof(C), typeof(B)}; 

List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result5 = ExtractElements (list, specifiers5); 

list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 
A[] result6 = ExtractElements (list, specifiers6); 

Результаты этого фрагмента кода будет:

result5 is {B, D}, list is {A, C, A} 
result6 is empty array, list is {B, A, D, C, A} 

Я знаю, что LINQ путь осуществить это, но, к сожалению, у меня нет опыта работы с ним.

ответ

1

Как насчет:

public IEnumerable<TType> ExtractElements<TType>(IEnumerable<TType> list, IEnumerable<Type> specifiers) { 
    var specifiersList = specifiers.ToList(); 

    return list.Where(t => specifiersList.Any(s => s.IsAssignableFrom(t.GetType()))); 
} 

var specifiers5 = new[] {typeof(B), typeof(D)}; 
var list = new List<A> {new B(), new A(), new D(), new C(), new A()}; 

// you can call ToArray() if you want but ForEach won't be available on that 
// and you'll need a standard foreach() loop 
var result5 = ExtractElements(list, specifiers5).ToList(); 

result5.ForEach(Console.WriteLine); 

Для more information на Type.IsAssignableFrom()

+0

Появляется, чтобы сделать трюк. У меня есть консольное приложение с рабочим примером. –

0

Это звучит как идеально подходит для Enumerable.OfType<T>().

+0

Я думаю, что вы, возможно, ударили его прямо по голове. Удаление моего ответа. По какой-то причине я боялся использовать этот метод, но новый об этом все время. Я думал о вызове Enumerable.Cast , который, как я знал, потенциально клюнул бы во время выполнения. –

+0

Ой, подождите, кроме единственной проблемы, что у него есть несколько типов, которые он хочет проверить. –

+0

А, справа. Это не делает ключ во всем этом, не так ли. (подтвердил ваш ответ) – hometoast

0

Это должно получить все, что вы хотели. Я добавил необязательный аргумент в метод ExtractElements, который позволяет включать/отключать соответствие порядка.

public static A[] ExtractElements (List<A> list, Type[] specifiers, bool orderMatters = false) 
{ 
    var allFound = true; 
    var listBackup = list.ToList(); // Make a backup copy 
    var returnList = new List<A>(); 
    var earliestMatch = 0; 

    foreach (var spec in specifiers) 
    { 
     var item = list.FirstOrDefault (i => spec.IsAssignableFrom(i.GetType())); 
     if (item != null) 
     { 
      var matchPosition = list.IndexOf(item); 
      if (orderMatters && matchPosition < earliestMatch) // we have an out of order match 
      { 
       allFound = false; 
       break; 
      } 
      earliestMatch = matchPosition; 
      list.Remove(item); 
      returnList.Add(item); 
     } 
     else 
     { 
      allFound = false; 
      break; 
     } 
    } 

    if(!allFound) 
    { 
     // Can't just assign list to listBackup because we have to update the 
     // underlying values not the reference that was passed to the function. 
     list.Clear(); 
     listBackup.ForEach(i => list.Add(i)); 
     returnList.Clear(); 
    } 

    return returnList.ToArray(); 
} 

Я предлагаю захватывая копию LinqPad, чтобы помочь вам проверить любые LINQ заявления и узнать LINQ в целом.

Надеюсь, это поможет.

+0

О, вау ... Я просто понял, что он хочет изменить входящую коллекцию. Я просто позволю тебе взять это отсюда. –

+0

Да, это сделало его гораздо более неудобным. –

+0

Без сомнения. –

Смежные вопросы