IEnumerable FirstOrEmpty Extension



>问题

我正在寻找一种为 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);

相关内容

  • 没有找到相关文章

最新更新