带有默认值的 C# LINQ SelectMany
C# LINQ SelectMany with Default
我正在寻找一种优雅的解决方案,以将集合中的子集合聚合成一个大集合。我的问题是某些子集合可能为空。
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]);
两者都可以,但我正在对父级的相当多的子集合执行特定操作,而且它变得有点笨拙。
我想要的是创建一个扩展方法来检查集合是否为空,如果是则执行选项 2 的操作 - 添加一个空数组。但是我对 Func 的理解还没有达到知道如何编写此扩展方法的程度。我知道我想要的语法是这样的:
var aggregatedChildCollection = parentCollection.SelectManyIgnoringNull(x => x.ChildCollection);
是否有一种简单的扩展方法可以实现此目的?
您可以使用自定义扩展方法:
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);
如果 ParentCollection
是您自己的 class,您还应该能够为 class 添加默认构造函数,例如:
public ParentCollection{
public ParentCollection() {
ChildCollection = new List<ChildCollection>();
}
}
这应该可以防止 null ref 异常,如果其中没有任何内容,则会为您提供一个空列表。至少这适用于 EF 模型。
你的 "option 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());
您可以使用 SelectMany
Reference Source
避免 LINQ 扩展的开销
public static IEnumerable<TResult> SelectManyNotNull<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{
foreach (TSource element in source)
{
var subElements = selector(element);
if (subElements != null)
foreach (TResult subElement in subElements )
yield return subElement;
}
}
我正在寻找一种优雅的解决方案,以将集合中的子集合聚合成一个大集合。我的问题是某些子集合可能为空。
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]);
两者都可以,但我正在对父级的相当多的子集合执行特定操作,而且它变得有点笨拙。
我想要的是创建一个扩展方法来检查集合是否为空,如果是则执行选项 2 的操作 - 添加一个空数组。但是我对 Func 的理解还没有达到知道如何编写此扩展方法的程度。我知道我想要的语法是这样的:
var aggregatedChildCollection = parentCollection.SelectManyIgnoringNull(x => x.ChildCollection);
是否有一种简单的扩展方法可以实现此目的?
您可以使用自定义扩展方法:
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);
如果 ParentCollection
是您自己的 class,您还应该能够为 class 添加默认构造函数,例如:
public ParentCollection{
public ParentCollection() {
ChildCollection = new List<ChildCollection>();
}
}
这应该可以防止 null ref 异常,如果其中没有任何内容,则会为您提供一个空列表。至少这适用于 EF 模型。
你的 "option 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());
您可以使用 SelectMany
Reference Source
public static IEnumerable<TResult> SelectManyNotNull<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{
foreach (TSource element in source)
{
var subElements = selector(element);
if (subElements != null)
foreach (TResult subElement in subElements )
yield return subElement;
}
}