关于c#:使用LINQ检查列表是否为空

Checking if a list is empty with LINQ

确定列表是否为空的"最佳"(同时考虑速度和可读性)方法是什么?即使列表是IEnumerable类型,并且没有count属性。

现在我在这两种情况之间辗转反侧:

1
if (myList.Count() == 0) { ... }

而这:

1
if (!myList.Any()) { ... }

我猜第二个选项更快,因为它一看到第一个项目就会返回结果,而第二个选项(对于IEnumerable)需要访问每个项目以返回计数。

也就是说,第二个选项对您来说是否可读?你喜欢哪一种?或者你能想出一个更好的方法来测试一个空列表吗?

edit@lassevk的响应似乎是最合乎逻辑的,再加上一些运行时检查,以便在可能的情况下使用缓存的计数,如下所示:

1
2
3
4
5
6
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;

    return !list.Any();
}


你可以这样做:

1
2
3
4
5
6
public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
    if (source == null)
        return true; // or throw an exception
    return !source.Any();
}

编辑:请注意,如果基础源实际上具有fast count属性,那么简单地使用.count方法将会很快。上面的一个有效的优化方法是检测一些基类型并简单地使用那些基类型的.count属性,而不是.any()方法,但如果不能保证,则返回到.any()。


我将对您似乎已经解决的代码做一个小的补充:也检查ICollection,因为它甚至由一些不过时的泛型类(即QueueStack)实现。我也会使用as,而不是is,因为它更惯用,而且速度更快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }

    var genericCollection = list as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == 0;
    }

    var nonGenericCollection = list as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == 0;
    }

    return !list.Any();
}


LINQ itself must be doing some serious optimization around the Count() method somehow.

这让你吃惊吗?我想对于IList实现,Count只是直接读取元素的数量,而Any必须查询IEnumerable.GetEnumerator方法,创建实例并至少调用一次MoveNext

编辑@ Matt:

I can only assume that the Count() extension method for IEnumerable is doing something like this:

是的,当然有。这就是我的意思。实际上,它使用ICollection而不是IList,但结果是相同的。


我刚刚写了一个快速测试,试试这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 IEnumerable<Object> myList = new List<Object>();

 Stopwatch watch = new Stopwatch();

 int x;

 watch.Start();
 for (var i = 0; i <= 1000000; i++)
 {
    if (myList.Count() == 0) x = i;
 }
 watch.Stop();

 Stopwatch watch2 = new Stopwatch();

 watch2.Start();
 for (var i = 0; i <= 1000000; i++)
 {
     if (!myList.Any()) x = i;
 }
 watch2.Stop();

 Console.WriteLine("myList.Count() =" + watch.ElapsedMilliseconds.ToString());
 Console.WriteLine("myList.Any() =" + watch2.ElapsedMilliseconds.ToString());
 Console.ReadLine();

第二个几乎慢了三倍。)

使用堆栈、数组或其他方案再次尝试秒表测试,这实际上取决于列表的类型,因为它们证明了计数要慢一些。

所以我想这取决于你使用的列表类型!

(只是指出,我把2000多个对象放在列表中,计数速度仍然更快,与其他类型相反)


根据微软的文档,List.Count是o(1):http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

所以只要使用List.Count == 0,它比查询快得多。

这是因为它有一个名为count的数据成员,每当列表中添加或删除某些内容时,它都会更新该成员,所以当您调用List.Count时,它不必遍历每个元素来获取它,它只返回数据成员。


@Konrad让我吃惊的是,在我的测试中,我将列表传递给一个接受IEnumerable的方法,因此运行时无法通过调用IList的count()扩展方法来优化它。

我只能假设IEnumerable的count()扩展方法正在执行以下操作:

1
2
3
4
5
6
7
8
public static int Count<T>(this IEnumerable<T> list)
{
    if (list is IList<T>) return ((IList<T>)list).Count;

    int i = 0;
    foreach (var t in list) i++;
    return i;
}

…换句话说,对IList的特殊情况进行一点运行时优化。

/编辑@konrad+1 mate-你说得对,很可能是在ICollection上。


如果有多个项目,第二个选项会更快。

  • 一找到1个项目,Any()就返回。
  • Count()必须不断地检查整个清单。

例如,假设枚举有1000个项。

  • Any()会检查第一个,然后返回true。
  • 遍历整个枚举后,Count()将返回1000。

如果使用其中一个谓词重写,则情况可能更糟-count()仍然必须检查每个项,即使只有一个匹配项。

你习惯了使用任何一个-它确实有意义并且可读。

一个警告是-如果您有一个列表,而不仅仅是一个IEnumerable,那么使用该列表的Count属性。


另一个想法:

1
if(enumerable.FirstOrDefault() != null)

不过,我更喜欢any()方法。


这对于使其与实体框架协同工作至关重要:

1
2
3
4
5
6
var genericCollection = list as ICollection<T>;

if (genericCollection != null)
{
   //your code
}


好的,那么这个怎么样?

1
2
3
4
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable.GetEnumerator().MoveNext();
}

编辑:我刚刚意识到有人已经画出了这个解决方案的草图。有人提到any()方法可以做到这一点,但为什么不自己做呢?当做


如果我用count()检查linq在数据库中执行"select count(*)…",但我需要检查结果是否包含数据,我决定引入firstordefault()而不是count();

以前

1
2
3
4
5
6
7
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

if (cfop.Count() > 0)
{
    var itemCfop = cfop.First();
    //....
}

1
2
3
4
5
6
7
8
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

var itemCfop = cfop.FirstOrDefault();

if (itemCfop != null)
{
    //....
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private bool NullTest<T>(T[] list, string attribute)

    {
        bool status = false;
        if (list != null)
        {
            int flag = 0;
            var property = GetProperty(list.FirstOrDefault(), attribute);
            foreach (T obj in list)
            {
                if (property.GetValue(obj, null) == null)
                    flag++;
            }
            status = flag == 0 ? true : false;
        }
        return status;
    }


public PropertyInfo GetProperty<T>(T obj, string str)

    {
        Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
            .GetType().GetProperties().ToList()
            .Find(property => property.Name
            .ToLower() == Column
            .ToLower()).Name.ToString());
        return GetProperty.Compile()(obj, str);
    }

下面是我对丹涛答案的实现,允许一个谓词:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any(predicate);
}

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any();
}

private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
{
    var genericCollection = source as ICollection<TSource>;
    if (genericCollection != null) return genericCollection.Count == 0;
    var nonGenericCollection = source as ICollection;
    if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
    return false;
}

1
2
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;


埃多克斯1〔23〕。这就是全部


此扩展方法适用于我:

1
2
3
4
5
6
7
8
9
10
11
12
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}