2016-09-05 4 views
2

Я ищу элегантное решение для объединения детской коллекции в коллекцию в одну большую коллекцию. Моя проблема в том, что некоторые дочерние коллекции могут быть пустыми.C# LINQ SelectMany по умолчанию

EG:

var aggregatedChildCollection = parentCollection.SelectMany(x=> x.ChildCollection); 

Это бросает исключение если какой-либо из объектов сбора ребенок не может быть пустым. Некоторые альтернативы:

// option 1 
var aggregatedChildCollection = parentCollection 
    .Where(x=>x.ChildCollection != null) 
    .SelectMany(x => x.ChildCollection); 

// option 2 
var aggregatedChildCollection = parentCollection 
    .SelectMany(x => x.ChildCollection ?? new TypeOfChildCollection[0]); 

И будет работать, но я делаю определенную операцию на очень немногих детских коллекций на родителей, и она становится немного unweilding.

Я хотел бы создать метод расширения, который проверяет, является ли коллекция нулевым, и если это делает то, что делает опция 2 - добавляет пустой массив. Но мое понимание Func не в такой ситуации, когда я знаю, как кодировать этот метод расширения. Я знаю, что синтаксис, я хотел бы, как это:

var aggregatedChildCollection = parentCollection.SelectManyIgnoringNull(x => x.ChildCollection); 

Есть простой метод расширения, что бы это сделать?

ответ

2

Вы можете использовать метод пользовательского расширения:

public static IEnumerable<TResult> SelectManyIgnoringNull<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TResult>> selector) 
{ 
    return source.Select(selector) 
     .Where(e => e != null) 
     .SelectMany(e => e); 
} 

И использовать так:

var aggregatedChildCollection = parentCollection 
    .SelectManyIgnoringNull(x => x.ChildCollection); 
+0

Отличное решение - в моем случае я объединил его с ответом Кори Нельсона, который использует 'Enumerable.Empty ()' вместо того, чтобы вырезать нулевые коллекции, чтобы у меня всегда была как минимум пустая коллекция. – DenverCoder9

+0

Это также работает, но помните, что Кори связан с кодом, который имеет лицензию, поэтому вам нужно будет указать атрибуцию. – DavidG

1

Если ParentCollection ваш собственный класс, который вы также должны быть в состоянии конструктор по умолчанию для класса такого as:

public ParentCollection{ 
    public ParentCollection() { 
     ChildCollection = new List<ChildCollection>(); 
    } 
} 

Это должно предотвратить исключение null ref и дать вам пустой список, если в нем ничего нет. По крайней мере, это работает с EF-моделями.

+2

Нет, к сожалению, я не могу гарантировать, что какая-либо из детских коллекций будет заполнена. Дело не в том, что класс недействителен, если дочерняя коллекция имеет значение null, я просто хочу сгруппировать все дочерние коллекции в одну коллекцию и игнорировать любые нули, рассматривая их как пустую коллекцию. – DenverCoder9

1

Ваша «опция 2» - это то, что я хотел бы сделать, с незначительной настройкой: используйте Enumerable.Empty() вместо создания пустого массива, чтобы уменьшить количество новых объектов, которые вы создаете.

Я использую тривиальный метод расширения Touch() - имя утилиты * NIX - сохранить поток синтаксиса LINQ и уменьшить ввод:

public static IEnumerable<T> Touch<T>(this IEnumerable<T> items) => 
    items ?? Enumerable.Empty<T>(); 

И будет использовать его как:

var aggregatedChildCollection = parentCollection 
    .SelectMany(x => x.ChildCollection.Touch()); 
1

Вы можете избежать накладных расходов с расширением LINQ, используя SelectManyReference Source

public static IEnumerable<TResult> SelectManyNotNull<TSource, TResult>(
     this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) 
{ 
    foreach (TSource element in source) 
     if (selector(element) != null) 
      foreach (TResult subElement in selector(element)) 
       yield return subElement; 
} 
+0

Спасибо за это - хороший ресурс и интересно посмотреть, как они это делают. – DenverCoder9

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