>问题
我正在寻找一种为 X 实现IEnumerable<T>
IEnumerable<X>
实现FirstOrEmpty
的方法。 基本上,如果谓词不匹配任何内容,则返回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) { ... }
尝试
尝试 1:
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;
}
}
}
尝试 2:
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;
}
您自己的解决方案:
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)
是某种结构,TSource
可能不会null
。你可以这样称呼它:
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);
工程。
我可能会使用尝试 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>();
}
编辑:我很想知道为什么这个答案被否决了。
我想出了与@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);