C#类库集合ObservableCollection<T>与Collection<T>的区别

8
我正在创建一个类库,可以在多种情况下使用,如ASP.NET、Console Apps、其他类库和XAML目标(如Silverlight或WPF)。
最初我决定将集合公开为IList。但当我编写使用XAML的示例时,我发现如果要轻松地绑定到这些集合,则需要使用ObservableCollection。
我的选择是什么?
我可以让库公开ObservableCollection并强制用户使用它,即使用户与XAML无关。这是一个坏事吗?
我可以使我的类成为通用的,允许调用者指定他们想要的集合类型,只要它实现了ICollection,可能默认为Collection。
我可以制作一组类,其中一个使用ObservableCollection,另一个不使用,称为Foo和ObservableFoo。
当我的类实现INotifyCollectionChanged时,这似乎很愚蠢,因为ObservableCollection已经为我做了这个操作。
显然,我试图保持代码简洁明了,但支持数据绑定似乎很重要。
有什么建议吗?
编辑:尝试使用两种替代方案创建可移动类库项目。
在Foo类中,我有:
    private readonly Collection<string> strings = new Collection<string>();

    public ReadOnlyCollection<string> Strings
    {
        get
        {
            return new ReadOnlyCollection<string>(this.strings);
        }
    }

在ObservableFoo类中,我有
    private readonly ObservableCollection<string> strings = new ObservableCollection<string>();

    public ReadOnlyObservableCollection<string> Strings
    {
        get
        {
            return new ReadOnlyObservableCollection<string>(this.strings);
        }
    }

这个非常简单的单元测试代码是

    [TestMethod]
    public void TestMethod1()
    {
        var foo = new ObservableFoo(); // or new Foo()

        Assert.AreNotEqual(0, foo.Id);
        Assert.AreNotEqual(0, foo.Strings.Count);
    }

唯一的缺点是当我使用 ReadOnlyObservableCollection 时,测试项目出现了编译错误。
引用程序集“System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes”中定义的类型“System.Collections.ObjectModel.ReadOnlyObservableCollection`1”。必须添加对该程序集的引用。
因此,在这种情况下,使用 ReadOnlyObservableCollection 将会强制用户添加对 System.Windows 的引用,这是一个缺点。
编辑:我想到了一个解决方案,发布在我的博客上 - 请参见如何使库同时具有可移植性和数据绑定友好性?

我认为如果您提供一个简洁的代码示例会很有帮助。 - Strelok
2个回答

3

这要看情况而定。如果你编写的是一个纯模型库,暴露WPF特定的接口就没有意义了。这会强制用户与WPF库链接,即使他们不需要也一样。即使如此,暴露出来的内容也是用户不需要的,这在我看来不是一个好的设计。

如果你的库不仅限于模型使用,我建议将其拆分成几个部分:核心部分适用于所有使用场景,带有WPF特定接口的WPF依赖部分,可能还有带有ASP特定功能的ASP特定部分等。用户将选择所需的部分并使用它们。


编辑:正如@Julien的评论所述,ObservableCollection<T>现在作为核心的一部分,因此包含它不会使用户依赖于WPF特定的库。尽管如此,这个想法仍然是一样的。对于WPF使用,通常需要提供/处理特定功能(ObservableCollection, INotifyPropertyChanged/DependencyObject, 依赖属性,仅在UI线程中进行通知等)。这意味着它们属于项目的一个单独的、WPF特定的部分。

因此,你可以将库分为几个部分:Library.Core.dll包含适用于通用/模型开发的函数,Library.WPF.dll处理WPF特定的东西并使用Library.Core.dll,也许还有Library.Console.dll和Library.ASP.dll。WPF的用户将使用Library.Core.dll和Library.WPF.dll,控制台程序可能需要Library.Core.dll和Library.Console.dll等。


我同意你的帖子,但是需要指出自从.NET 4以来,ObservableCollection和其他类似类型已经从WindowsBase.dll移动到System.dll,因此它们不再仅限于WPF。 - Julien Lebosquain
1
那么...如果ObservableCollection不是WPF特定的,而且没有人向INotifyCollectionChanged接口添加事件处理程序,也许使用这种类型作为集合类型的想法并不那么糟糕? - Ron Jacobs

3
我认为你的库的底层组件应该提供合适的接口,以适应特定抽象级别下的使用,而不考虑API的各种使用方需求。
例如,如果一个组合WPF应用程序将使用你的组件,那么将这些组件封装到一个Model或View Model中,以将你的组件提供的>(更好的是IEnumerable )适配到适合绑定到视图的ObservableCollection 中是完全合适的。
控制台应用程序可能不需要这样的开销,可以愉快地使用IEnumerable。
另外,即使作为IList公开集合时也要小心。这允许你的库的使用方向列表添加和删除条目,这可能不符合接口的精神。

我考虑过这个问题,但是用ObservableCollection包装一个IEnumerable<string>需要用户将每个字符串复制到ObservableCollection中,这似乎不太优化。 - Ron Jacobs
没错,但如果调用者要求集合能够被视图观察(暗示集合随时间而变化),他们将会定期调用(轮询)你的组件以获取最新的列表,并相应地更新他们的集合,因此无论如何都会存在开销。当然,如果你的组件通知消费者有关集合变化的信息是有意义的,那么向调用者提供ObservableCollection是完全有道理的。 - lesscode
1
@RonJacobs,让所有不需要可观察能力的消费者承担这些能力所带来的成本是否不是最优的选择? - Rune FS
在浏览ObservableCollection<T>的源代码后,如果没有人订阅通知,我怀疑额外的开销可能非常小,除非集合中的项目数量非常大。在我的情况下,集合中的项目数量几乎总是少于100个。 - Ron Jacobs
@RonJacobs,同样适用于您试图避免的成本(创建少于100个项的新集合)。 - Rune FS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接