[聚合文章] 讲解.NET 集合中使用Count属性和扩展方法Count<T>()区别

.Net 2017-11-12 1 阅读

.NET中System.Linq命名空间中有个扩展方法叫 Count<T>() ,现在看下面的代码示例:

    class Program
    {
        static void Main(string[] args)
        {
            var userList = new List<int>();
            userList.Add(1);
            userList.Add(2);
            userList.Add(3);
            userList.Add(4);
            userList.Add(5);
            userList.Add(6);
            Console.WriteLine(userList.Count);
            Console.WriteLine(userList.Count());
        }

    }

上门代码通过调用了List的自身属性Count,然后又调用了Linq支持的扩展方法Count(),结果都是输出的6,可是它们背后有哪些不一样呢?

我们先开Count本身的属性,这里只粘贴了核心源码,完整的地址自行可以查看 http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,aeb6ba6c11713802

[ContractPublicPropertyName("Count")]
       private int _size;
 public int Count {
            get {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _size; 
            }
        }
         public void Add(T item) {
            if (_size == _items.Length) EnsureCapacity(_size + 1);
            _items[_size++] = item;
            _version++;
        }

我们通过上面可以看到,内部维护了一个_size字段来表示List中的数量,再向集合中添加元素时,已经进行统计过了。这时再取集合的数量时,时间复杂度就为O(1)。

然后我们在看下扩展方法 Count<T> 源代码如下:

public static int Count<TSource>(this IEnumerable<TSource> source) {
            if (source == null) throw Error.ArgumentNull("source");
            ICollection<TSource> collectionoft = source as ICollection<TSource>;
            if (collectionoft != null) return collectionoft.Count;
            ICollection collection = source as ICollection;
            if (collection != null) return collection.Count;
            int count = 0;
            using (IEnumerator<TSource> e = source.GetEnumerator()) {
                checked {
                    while (e.MoveNext()) count++;
                }
            }
            return count;
        }

我们通过上面可以看到传入的集合元素如果不能转换为 ICollection<T> 和ICollection 便会进行逐个元素进行遍历,然后返回最终的元素集合,这样的时间复杂度当然为O(n)。所以能直接调用集合属性获取数量,就尽量避免使用扩展方法 Count<T>

说到这里,可能有的小伙伴问了,为何 List<T> 不能转换为 ICollection<T>

我们来看一下 public interface ICollection<T> : IEnumerable<T>, IEnumerable 这个 ICollection<T> 继承自 IEnumerable<T> , 那么让一个父类转换为一个子类,真是不可思议,理所当然这里一直就是null, 不知道是不是我理解的错误,烦请小伙伴指正? 同理 ICollection : IEnumerable ,哪里来的成功?最终只能逐个进行遍历。

当然这里有一个中间方法,就是把userList 先转换为 ICollection<int> ,然后在去调用 Count<>() 方法,这样就能直接返回对应的个数。如下代码:

static void Main(string[] args)
        {
            var userList = new List<int>();
            userList.Add(1);
            userList.Add(2);
            userList.Add(3);
            userList.Add(4);
            userList.Add(5);
            userList.Add(6);
            ICollection<int> collection = userList as ICollection<int>;
            Console.WriteLine(collection.Count());


        }

看到上面的中间方法,我们可以进一步了解到,作为一个公共方法肯定要满足大多数的需要(可以参考我这篇文章: http://blog.csdn.net/u010533180/article/details/50627771 ),所以这里使用了顶级的父类,就有了中间的两层转换操作,这也是基于性能方面考虑,比如传递过来的就是继承了ICollection接口,这样直接取出数量即可,不过大多数情况我们直接使用的是继承自 IEnumerable<T> 接口的类,所以我们作为代码的搬运工要积极思考微软这套东西,持有怀疑性精神前进,这样我们会了解到更多的东西。

最后我想说的是我们要通过提供的源代码,积极优化我们书写的代码,哪怕一个稍微的转换,对你本身代码的性能就是一个很大的提升。

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。