我正在寻找一种方法来为FirstOrEmpty
实施IEnumerable<X>
,其中X实现IEnumerable<T>
。基本上,如果谓词与任何内容都不匹配,请返回Enumerable<T>.Empty
。我不想将源参数限制为IEnumerable<IEnumerable<X>>
,或者因为我可能想要传递实现IEnumerable<X>
的内容(例如IEnumerable<IGrouping<bool, X>>
)。
IEnumerable<T> myCollection;
var groupCheck = myCollection.GroupBy(t => t.SomeProp == 23);
var badGroup = groupCheck.FirstOrEmpty(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty(t => t.Key);
foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }
旧方式:
IEnumerable<T> myCollection = ...;
var groupCheck = myCollection.GroupBy(t => t.SomePropOnClassT == 23);
var badGroup = (groupCheck.FirstOrDefault(t => !t.Key) ?? Enumerable<T>.Empty);
var goodGroup = (groupCheck.FirstOrDefault(t => t.Key) ?? Enumerable<T>.Empty);
foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
TSource tmp = source.FirstOrDefault(predicate);
if(tmp != null) {
foreach(TResult x in tmp)
{
yield return x;
}
}
}
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
TSource tmp = source.FirstOrDefault(predicate);
return tmp == null ? Enumerable.Empty<TResult>() : tmp;
}
答案 0 :(得分:4)
您自己的解决方案:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
where TSource : class, IEnumerable<TResult>
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
实际上正在运作。我只添加了class
约束。这意味着“TSource
必须是引用类型”,并且接口可以使用class
约束。这样做的原因是default(TSource)
如果null
是某个结构,则可能不是TSource
。你可以这样称呼它:
var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => g.Key);
不幸的是,编译器不够聪明,无法弄清楚类型参数本身,所以你必须像上面那样在尖括号<..., ...>
中提供它们。我还没有找到解决办法。
现在,如果您始终使用IGrouping<,>
,则可能需要使用以下稍微不那么通用的方法:
public static IEnumerable<TResult> FirstOrEmpty<TKey, TResult>(
this IEnumerable<IGrouping<TKey, TResult>> source,
Func<IGrouping<TKey, TResult>, bool> predicate
)
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
这一次是这样的:
var badGroup = groupCheck.FirstOrEmpty<bool, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<bool, T>(g => g.Key);
并且好消息是,使用这种方法,编译器将推断出你的类型参数,所以:
var badGroup = groupCheck.FirstOrEmpty(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty(g => g.Key);
作品。
答案 1 :(得分:2)
我可能会使用重构版本的尝试2:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : class, IEnumerable<TResult>
{
return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
编辑:我很想知道为什么这个答案被低估了。
答案 2 :(得分:1)
我想出了与@phoog相同的结果,但是很难让编译器推断出类型参数:
public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
return (IEnumerable<TResult>)source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}
我想出的最好是明确说明:
var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => t.Key);