为什么ObservableCollection<;T>;有两个集合构造函数

本文关键字:两个 集合 构造函数 lt ObservableCollection gt 为什么 | 更新日期: 2023-09-27 18:19:52

ObservableCollection(T)类有两个构造函数,其中可以传递项的集合。一个构造函数接受IEnumerable(T),而另一个构造函数则接受List(T)

既然List(T)实现了IEnumerable(T),为什么存在第二个构造函数?

为什么ObservableCollection<;T>;有两个集合构造函数

采用List的构造函数是在.NET 3.0中引入的,而采用IEnumerable的构造函数直到.NET 3.5才引入,因此删除List构造函数将是一个巨大的变化。

我猜他们写它是为了接受List,然后发货,后来意识到它可以更通用。

源代码也有一个有趣的注释(不确定它是否相关):

    public ObservableCollection(List<T> list)
        : base((list != null) ? new List<T>(list.Count) : list)
    {
        // Workaround for VSWhidbey bug 562681 (tracked by Windows bug 1369339).
        // We should be able to simply call the base(list) ctor.  But Collection<T>
        // doesn't copy the list (contrary to the documentation) - it uses the
        // list directly as its storage.  So we do the copying here.
        // 
        CopyFrom(list);
    }
    public ObservableCollection(IEnumerable<T> collection)
    {
        if (collection == null)
            throw new ArgumentNullException("collection");
        CopyFrom(collection);
    }

看看这里的来源:

http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs,f63ea2601f5edbbb

如果不挖得太深,你会注意到:

public ObservableCollection(List<T> list)
    : base((list != null) ? new List<T>(list.Count) : list)
{
    // Workaround for VSWhidbey bug 562681 (tracked by Windows bug 1369339).
    // We should be able to simply call the base(list) ctor.  But Collection<T>
    // doesn't copy the list (contrary to the documentation) - it uses the
    // list directly as its storage.  So we do the copying here.
    // 
    CopyFrom(list);
}

这个:

public ObservableCollection(IEnumerable<T> collection)
{
    if (collection == null)
        throw new ArgumentNullException("collection");
    CopyFrom(collection);
}

请注意,在传递List<T>的情况下,它会用一个新的List<T>调用基构造函数,它的容量已经设置为与您传递的列表的Count相匹配。显然,您不能用IEnumerable<T>来实现这一点,因为IEnumerable<T>没有Count属性。所以它看起来像是一个优化。如果您已经知道集合将有多大,那么当您将源复制到内部List<T>时,可以避免由于调整其大小而造成的损失。

编辑:还有@DStanley关于打破变化的观点。无论如何,这可能更重要再次编辑:或者不编辑,因为文档中似乎有一些混乱。但我创建了一个以.NET 3.0为目标的项目,看起来两个构造函数都在那里。如果我在Visual Studio中使用"转到定义",我可以看到:

#region Assembly WindowsBase.dll, v3.0.0.0
// C:'Program Files (x86)'Reference Assemblies'Microsoft'Framework'v3.0'WindowsBase.dll
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
namespace System.Collections.ObjectModel
{
    [Serializable]
    public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        public ObservableCollection();   
        public ObservableCollection(IEnumerable<T> collection);
        public ObservableCollection(List<T> list);
        //....

而这段代码,在.NET 3.0中编译得很好:

var list = new List<int>() {1,2,3};
IEnumerable<int> enumerable = (IEnumerable<int>)list;
var obsWithList = new ObservableCollection<int>(list);
var obsWithEnumerable = new ObservableCollection<int>(enumerable);

经过一点反思:

var internalListProp = typeof(ObservableCollection<int>).GetProperty("Items", BindingFlags.NonPublic | BindingFlags.Instance);
var l0 = (internalListProp.GetValue(obsWithList, null) as List<int>).Capacity;        // 3
var l1 = (internalListProp.GetValue(obsWithEnumerable, null) as List<int>).Capacity;  // 4

您可以看到,当传入List<T>时,内部列表的容量被设置为与Count相匹配,但当传递IEnumerable<T>时则不匹配。