IEnumerable vs IReadonlyCollection vs ReadonlyCollection用于公开列表成员 [英] IEnumerable vs IReadonlyCollection vs ReadonlyCollection for exposing a list member

查看:86
本文介绍了IEnumerable vs IReadonlyCollection vs ReadonlyCollection用于公开列表成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了好几个小时思考一下揭露名单成员的话题.在与我类似的问题中,乔恩·斯基特(Jon Skeet)给出了一个很好的答案.请随时查看.

I have spent quite a few hours pondering the subject of exposing list members. In a similar question to mine, Jon Skeet gave an excellent answer. Please feel free to have a look.

ReadOnlyCollection或IEnumerable用于公开成员集合?

通常,我对公开列表非常偏执,尤其是在开发API的情况下.

I am usually quite paranoid to exposing lists, especially if you are developing an API.

我一直使用IEnumerable公开列表​​,因为它很安全,并且具有很大的灵活性.让我在这里举个例子:

I have always used IEnumerable for exposing lists, as it is quite safe, and it gives that much flexibility. Let me use an example here:

public class Activity
{
    private readonly IList<WorkItem> workItems = new List<WorkItem>();

    public string Name { get; set; }

    public IEnumerable<WorkItem> WorkItems
    {
        get
        {
            return this.workItems;
        }
    }

    public void AddWorkItem(WorkItem workItem)
    {
        this.workItems.Add(workItem);
    }
}

任何使用IEnumerable进行编码的人在这里都是非常安全的.如果以后我决定使用有序列表或其他东西,它们的代码都不会中断,而且还是不错的. IEnumerable的缺点是可以将其转换回此类之外的列表.

Anyone who codes against an IEnumerable is quite safe here. If I later decide to use an ordered list or something, none of their code breaks and it is still nice. The downside of this is IEnumerable can be cast back to a list outside of this class.

因此,许多开发人员使用ReadOnlyCollection公开成员.这是非常安全的,因为它永远不会被投射回列表.对我来说,我更喜欢IEnumerable,因为它提供了更大的灵活性,如果我想实现除列表之外的其他功能.

For this reason, a lot of developers use ReadOnlyCollection for exposing a member. This is quite safe since it can never be cast back to a list. For me I prefer IEnumerable since it provides more flexibility, should I ever want to implement something different than a list.

我想出了一个我更喜欢的新主意.使用IReadOnlyCollection:

I have come up with a new idea I like better. Using IReadOnlyCollection:

public class Activity
{
    private readonly IList<WorkItem> workItems = new List<WorkItem>();

    public string Name { get; set; }

    public IReadOnlyCollection<WorkItem> WorkItems
    {
        get
        {
            return new ReadOnlyCollection<WorkItem>(this.workItems);
        }
    }

    public void AddWorkItem(WorkItem workItem)
    {
        this.workItems.Add(workItem);
    }
}

我觉得这保留了IEnumerable的一些灵活性,并且封装得很好.

I feel this retains some of the flexibility of IEnumerable and is encapsulated quite nicely.

我发布了这个问题,以征询我的想法.您是否喜欢此解决方案而不是IEnumerable?您认为使用ReadOnlyCollection的具体返回值更好吗?这是一场辩论,我想尝试看看我们所有人可以提出哪些优点/缺点.

I posted this question to get some input on my idea. Do you prefer this solution to IEnumerable? Do you think it is better to use a concrete return value of ReadOnlyCollection? This is quite a debate and I want to try and see what are the advantages/disadvantages that we all can come up with.

谢谢您的输入.

EDIT

EDIT

首先,感谢大家为此处的讨论做出了巨大贡献.我当然从每个人身上都学到了很多东西,并衷心感谢您.

First of all thank you all for contributing so much to the discussion here. I have certainly learned a ton from each and every one and would like to thank you sincerely.

我要添加一些其他方案和信息.

I am adding some extra scenarios and info.

使用IReadOnlyCollection和IEnumerable有一些常见的陷阱.

There are some common pitfalls with IReadOnlyCollection and IEnumerable.

考虑以下示例:

public IReadOnlyCollection<WorkItem> WorkItems
{
    get
    {
        return this.workItems;
    }
}

即使接口是只读的,也可以将上面的示例转换回列表并进行更改.尽管具有相同的名称,但该接口不能保证不变性.由您提供一个不变的解决方案,因此您应该返回一个新的ReadOnlyCollection.通过创建一个新列表(本质上是一个副本),对象的状态是安全无虞的.

The above example can be casted back to a list and mutated, even though the interface is readonly. The interface, despite it's namesake does not guarantee immutability. It is up to you to provide an immutable solution, therefore you should return a new ReadOnlyCollection. By creating a new list (a copy essentially), the state of your object is safe and sound.

Richiban在他的评论中说得最好:界面仅保证某些事情可以做,而不保证它不能做.

Richiban says it best in his comment: a interface only guarantees what something can do, not what it cannot do.

请参见以下示例:

public IEnumerable<WorkItem> WorkItems
{
    get
    {
        return new List<WorkItem>(this.workItems);
    }
}

上面的内容可以进行强制转换和突变,但是您的对象仍然是不变的.

The above can be casted and mutated, but your object is still immutable.

框框语句之外的另一个是集合类.请考虑以下内容:

Another outside the box statement would be collection classes. Consider the following:

public class Bar : IEnumerable<string>
{
    private List<string> foo;

    public Bar()
    {
        this.foo = new List<string> { "123", "456" };
    }

    public IEnumerator<string> GetEnumerator()
    {
        return this.foo.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

上面的类可以具有按所需方式对foo进行突变的方法,但是您的对象永远不能转换为任何种类的列表并进行突变.

The class above can have methods for mutating foo the way you want it to be, but your object can never be casted to a list of any sort and mutated.

CarstenFührmann对IEnumerables中的收益回报表提出了一个奇妙的观点.

Carsten Führmann makes a fantastic point about yield return statements in IEnumerables.

再次感谢大家.

推荐答案

关于类库,我认为IReadOnly *确实有用,并且我认为您做对了:)

Talking about class libraries, I think IReadOnly* is really useful, and I think you're doing it right :)

这都是关于不可变集合的……在出现不可变元素和扩大数组之前,这是一项艰巨的任务,因此.net决定在框架中包括一些不同的可变集合,这些集合为您实现了丑陋的东西,但是恕我直言它们并没有为您提供非常有用的不可变的正确方向,尤其是在高并发情况下,共享可变内容始终是PITA.

It's all about immutable collection... Before there were just immutables and to enlarge arrays was a huge task, so .net decided to include in the framework something different, mutable collection, that implement the ugly stuff for you, but IMHO they didn't give you a proper direction for immutable that are extremely useful, especially in a high concurrency scenario where sharing mutable stuff is always a PITA.

如果您今天检查其他语言,例如Objective-C,您会发现实际上规则完全相反!他们总是在不同的类之间交换不可变的集合,换句话说,接口公开了不可变的,并且在内部它们使用可变的集合(是的,他们当然有),相反,如果希望让外部人员更改集合,则可以公开适当的方法(如果该类是有状态的类).

If you check other today languages, such as objective-c, you will see that in fact the rules are completely inverted! They quite always exchange immutable collection between different classes, in other words the interface expose just immutable, and internally they use mutable collection (yes, they have it of course), instead they expose proper methods if they want let the outsiders change the collection (if the class is a stateful class).

因此,我对其他语言的这种小经验使我认为.net列表功能如此强大,但是由于某些原因,存在不可变集合:)

So this little experience that I've got with other languages pushes me to think that .net list are so powerful, but the immutable collection were there for some reason :)

在这种情况下,不是帮助接口的调用者,而是在更改内部实现时避免他更改所有代码,就像IList vs List一样,但是通过IReadOnly *,您可以保护自己您自己,您的类,以不正确的方式使用,以避免无用的保护代码,有时您也无法编写的代码(过去在某些代码中,我不得不返回完整列表的副本,以避免这个问题).

In this case is not a matter of helping the caller of an interface, to avoid him to change all the code if you're changing internal implementation, like it is with IList vs List, but with IReadOnly* you're protecting yourself, your class, to being used in not a proper way, to avoid useless protection code, code that sometimes you couldn't also write (in the past in some piece of code I had to return a clone of the complete list to avoid this problem).

这篇关于IEnumerable vs IReadonlyCollection vs ReadonlyCollection用于公开列表成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆