如果找到多个实例,则返回null

时间:2014-03-11 13:06:54

标签: linq c#-4.0

我有一个IEnumerable和一个谓词(Func),我正在编写一个方法,如果列表中只有一个实例与谓词匹配,则返回一个值。如果条件与none匹配,则找不到任何条件。如果标准与许多实例匹配,则谓词不足以成功识别所需记录。这两种情况都应该返回null。

在LINQ中表达这种情况的建议方法是什么,不会导致列表的多个枚举?

如果找到多个实例,LINQ运算符SingleOrDefault将抛出异常。 即使找到多个,LINQ运算符FirstOrDefault也将返回第一个。

MyList.Where(predicate).Skip(1).Any() 

...将检查歧义,但不会保留所需的记录。

似乎我最好的举动是从MyList.Where(谓词)中获取Enumerator并保留第一个实例,如果访问下一个项目失败,但它似乎有点冗长。

我错过了一些明显的东西吗?

2 个答案:

答案 0 :(得分:3)

"稍微冗长"选项对我来说似乎是合理的,并且可以很容易地被分离为单个扩展方法:

// TODO: Come up with a better name :)
public static T SingleOrDefaultOnMultiple<T>(this IEnumerable<T> source)
{
    // TODO: Validate source is non-null
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            return default(T);
        }
        T first = iterator.Current;
        return iterator.MoveNext() ? default(T) : first;
    }
}

答案 1 :(得分:0)

更新:这是一种更通用的方法,可能更具可重用性。

public static IEnumerable<TSource> TakeIfCountBetween<TSource>(this IEnumerable<TSource> source, int minCount, int maxCount, int? maxTake = null)
{
    if (source == null)
            throw new ArgumentNullException("source");
    if (minCount <= 0 || minCount > maxCount)
        throw new ArgumentException("minCount must be greater 0 and less than or equal maxCount", "minCount");
    if (maxCount <= 0)
        throw new ArgumentException("maxCount must be greater 0", "maxCount");
    int take = maxTake ?? maxCount;
    if (take > maxCount)
        throw new ArgumentException("maxTake must be lower or equal maxCount", "maxTake");
    if (take < minCount)
        throw new ArgumentException("maxTake must be greater or equal minCount", "maxTake");

    int count = 0;
    ICollection objCol;
    ICollection<TSource> genCol = source as ICollection<TSource>;
    if (genCol != null)
    {
        count = genCol.Count;
    }
    else if ((objCol = source as ICollection) != null)
    {
        count = objCol.Count;
    }
    else
    {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext() && ++count < maxCount);
        }
    }

    bool valid = count >= minCount && count <= maxCount;
    if (valid)
        return source.Take(take);
    else
        return Enumerable.Empty<TSource>();
}

用法:

var list = new List<string> { "A", "B", "C", "E", "E", "F" };
IEnumerable<string> result = list
    .Where(s => s == "A")
    .TakeIfCountBetween(1, 1);
Console.Write(string.Join(",", result)); // or result.First()