最佳实践列表/数组/ ReadOnlyCollection创建(和使用)

时间:2011-01-06 12:10:22

标签: c# .net generics immutability

我的代码充满了收藏 - 我想这不是一件不寻常的事情。但是,各种集合类型的使用并不明显,也不是微不足道的。通常,我想使用暴露“最佳”API的类型,并且语法噪音最小。 (有关类似问题,请参阅Best practice when returning an array of valuesUsing list arrays - Best practices。有一些指南建议在API 中使用哪些类型,但这些在普通(非API)代码中是不切实际的。

例如:

new ReadOnlyCollection<Tuple<string,int>>(
    new List<Tuple<string,int>> {
        Tuple.Create("abc",3),
        Tuple.Create("def",37)
    }
)

List是一种非常常见的数据结构,但以这种方式创建它们涉及相当多的语法噪声 - 并且它可能很容易变得更糟(例如字典)。事实证明,许多列表永远不会改变,或者至少从未扩展。当然ReadOnlyCollection引入了更多的句法噪音,它甚至没有传达我的意思;毕竟ReadOnlyCollection可以包装变异集合。有时我在内部使用数组并返回IEnumerable来表示意图。但是大多数这些方法都具有非常低的信噪比;这对理解代码至关重要。

对于公共API的所有代码的99%,没有必要遵循框架指南:但是,我仍然想要一个易于理解的代码和类型传达意图。

那么,处理使用小型集合来传递值的沼泽标准任务的最佳实践方法是什么?在可能的情况下,数组应优先于List吗?还有别的吗?什么是最好的方式 - 清洁,可读,合理有效 - 传递这样的小集合?特别是代码应该显而易见 尚未读取此问题,并且不想阅读大量API文档但仍然了解其意图。 最大限度地减少代码混乱也非常重要 - 所以像ReadOnlyCollection这样的事情充其量是可疑的。具有小表面的主要API的罗嗦类型没有错,但不是大型代码库中的一般做法。

在没有大量代码混乱(例如显式类型参数)的情况下传递值列表的最佳方法是什么,但仍能清楚地传达意图?

编辑:澄清说这是关于制作简短明了的代码,而不是关于公共API的。

5 个答案:

答案 0 :(得分:2)

在希望了解您的问题之后,我认为您必须区分您在课堂上创建和管理的内容以及您向外界提供的内容。

在您的课程中,您可以使用最适合您当前任务的任何内容(ListArrayDictionaryLinkedList对比的优缺点。 )。但这可能与您在公共财产或职能部门提供的内容无关。

在您的公共合同(属性和函数)中,您应该返回所需的最少类型(或更好的界面)。所以只是一些公共类型的IListICollectionIDictionaryIEnumerable。由于性能原因使用List<>而不是{{1}反之亦然。)

答案 1 :(得分:2)

<强>更新

所以,严格来说,这并不是 new ;但是这个问题使我确信go ahead and announce an open source project I've had in the works for a while(仍然是一项正在进行的工作,但其中有一些有用的东西),其中包括一个IArray<T>接口(以及实现,自然而然)我认为在这里捕获你想要的东西:索引,只读,甚至协变(奖金!)界面

一些好处:

  • 它不是ReadOnlyCollection<T>这样的具体类型,因此它不会将您绑定到特定的实现。
  • 它不仅仅是一个包装器(如ReadOnlyCollection<T>),所以它“真的”是只读的。
  • 它为一些非常好的扩展方法清除了道路。到目前为止,Tao.NET库只有两个(我知道,弱),但更多的是在路上。您也可以轻松制作自己的 - 只需从ArrayBase<T>(也在库中)派生,并覆盖this[int]Count属性即可。

如果这听起来很有意义,请随时check it out告诉我你的想法。


我不是100%清楚 你担心这种“语法噪音”:你的代码或调用代码?

如果您在自己的封装代码中容忍某些“噪音”,那么我建议包装T[]数组并展示恰好是IList<T>的{​​{1}}:< / p>

ReadOnlyCollection<T>

是的,你的结尾有一些噪音,但它并不坏。你公开的界面非常干净。

另一个选择是创建自己的界面something like IArray<T>,它包装class ThingsCollection { ReadOnlyCollection<Thing> _things; public ThingsCollection() { Thing[] things = CreateThings(); _things = Array.AsReadOnly(things); } public IList<Thing> Things { get { return _things; } } protected virtual Thing[] CreateThings() { // Whatever you want, obviously. return new Thing[0]; } } 并提供一个只获取索引器。然后揭露那个。这基本上和暴露T[]一样干净,但没有错误地传达可以通过索引设置项目的想法。

答案 2 :(得分:1)

如果我能帮助它,我不会传递Lists。一般来说,我还有其他一些东西正在管理有问题的集合,它暴露了集合,例如:

public class SomeCollection
{
    private List<SomeObject> m_Objects = new List<SomeObject>();

    // ctor
    public SomeCollection()
    {
        // Initialise list here, or wot-not/
    } // eo ctor


    public List<SomeObject> Objects { get { return m_Objects; } }
} // eo class SomeCollection

所以这就是传递的对象:

public void SomeFunction(SomeCollection _collection)
{
    // work with _collection.Objects
} // eo SomeFunction

我喜欢这种方法,因为:

1)我可以在ctor中填充我的值。他们在那里有任何人new SomeCollection

2)如果需要,我可以限制访问基础列表。在我的例子中,我暴露了所有,但你不必这样做。如果需要,可以将其设置为只读,或者在添加之前验证列表的添加内容。

3)很干净。 SomeCollection更容易阅读。

4)如果您突然意识到您的选择集合效率低下,您可以更改基础集合类型,而无需将其传递的所有位置更改为参数(您能想象可能遇到的问题)比方说,List<SomeObject>?)

答案 3 :(得分:0)

我同意。 IList与ReadOnly集合和Modifiable集合紧密结合。 IList应该继承自IReadOnlyList。

强制转换为IReadOnlyList不需要显式强制转换。向前投射会。

1

定义自己的类,它实现IEnumerator,在新构造函数中使用IList,具有索引的只读默认项属性,并且不包含任何可能允许我操作列表的属性/方法。

如果您以后想要允许像IReadOnlyCollection那样修改ReadOnly包装器,您可以创建另一个类,它是自定义ReadOnly Collection的包装器,并具有Insert / Add / Remove / RemoveAt / Clear / ...实现和缓存那些变化。

2

使用ObservableCollection / ListViewCollection并创建自己的自定义ReadOnlyObservableCollection包装器,如#1中未实现添加或修改属性和方法。

ObservableCollection可以绑定到ListViewCollection,使得对ListViewCollection的更改不会被推回到ObservableCollection中。但是,如果您尝试修改集合,则原始的ReadOnlyObservableCollection会抛出异常。

如果您需要向后/向前兼容性,请创建两个继承自这些类的新类。然后实现IBindingList并处理/翻译CollectionChanged事件(INotifyCollectionChanged事件)到适当的IBindingList事件。

然后,您可以将其绑定到较旧的DataGridView和WinForm控件,以及WPF / Silverlight控件。

答案 4 :(得分:0)

Microsoft已创建一个Guidelines for Collections文档,该文档提供了非常有用的DO和DON列表,可以解决您的大多数问题。

这是一长串,所以这里是最相关的:

  

与数组相比,DO更喜欢集合。

     

请勿在公共API中使用ArrayList或List。 (公共属性,公共参数和公共方法的返回类型)

     

请勿在公共API中使用哈希表或字典。

     

请勿在公共API中使用弱类型的集合。

     

请使用可能的最少专业化类型作为参数类型。大多数将集合作为参数的成员都使用IEnumerable接口。

     

使用ICollection或ICollection作为参数来避免访问Count属性。

     

请使用ReadOnlyCollection(ReadOnlyCollection的子类),或者在极少数情况下使用IEnumerable获取代表只读集合的​​属性或返回值。

最后一点指出,您不应该像您建议的那样避免使用ReadOnlyCollection。对于公众成员来说,这是一种非常有用的类型,它可以告知消费者他们正在访问的馆藏的局限性。