在C#封装系列系列

2023-09-04 01:38:26 作者:痞子英雄

由于3.0 C#有很大的语法糖一样自动特性里面很多简化实施的封装原则。如果你使用它的原子值这是一件好事,这样你就可以代替这样的封装模式:

Since 3.0 C# has great syntax sugar like auto-properties which a lot simplify implementation of encapsulation principle. This is good if you use it with atomic values, so you can replace encapsulation pattern like this:

private string _name;

public string Name 
{
  get { return _name; }
  set { _name = value; }
}

只有一个行:

with just one line:

public string FirstName  { get; set; }

我很喜欢这个伟大的功能,因为它节省了大量的开发时间。

I very like this great feature as it saves a lot of developers time.

但事情并非当您创建属性指向集合如此之大。 通常我看到两种方式实现集合属性。

But things are not so great when you create property that points to collection. Usually I see collection properties implemented in one of two ways.

1)如果没有自动特性在所有能够使用字段初始

1) Without auto-properties at all to be able to use field initializer:

private List<string> _names = new List<string>();

public List<string> Names
{
    get { return _names; }
}

2)使用自动属性。这种方法是确定的,如果类只有一个构造函数:

2) Using auto-properties. This approach is ok if class has only one constructor:

public List<string> Names { get; private set; }

public .ctor()
{
    Names = new List<string>();
}

但是,当你处理可变集合像列表这样的方式,你打破封装,因为这个属性的用户可以修改集合,没有让容器知道(甚至取代集合,如果你忘了做二传手私营)。

But when you deal with mutable collections like lists such a way, you break encapsulation, as user of this property can modify collection without letting container know (or even replace collection if you forget to make setter private).

对于我来说,对于封装收集模式正确执行集合封装应该是这样的:

As for me, regarding to Encapsulate Collection pattern correct implementation of collection encapsulation should look like this:

private readonly List<string> _names = new List<string>();

public ICollection<string> Names
{
    get { return new ReadOnlyCollection<string>(_names); }
}

public void Add_Name(string name)
{
    _names.Add(name);
}

public void Remove_Names(string name)
{
    _names.Remove(name);
}

public void Clear_Names()
{
    _names.Clear();
}

老实说,我不记得,如果我遇到这种实现在现实code,即使在框架来源。我想这是因为人是懒惰,避免写code这样的量只是为了封装只是一点点坚强。

Honestly, I do not remember if I've met this kind of implementation in the real code, even in framework sources. I think this is because people are lazy and avoid writing such amount of code just to make encapsulation just a little bit stronger.

我想知道为什么C#球队并没有提供定义集合自动性能有一定的清晰和简单的方法,使开发人员能够取悦自己的懒惰还是创造稳健的code?

I wondering why C# team does not provide some clear and easy way to define collection auto-properties, so developers can please their laziness still creating robust code?

推荐答案

TL; DR ,C#编译器不具有自动集合,因为有大量的馆藏露出不同的方式。当暴露一个集合,你应该好好想想要如何收集被封装,并使用正确的方法。

TL;DR, The C# compiler doesn't have auto-collections because there are lots of different ways of exposing collections. When exposing a collection you should think carefully about how you want the collection to be encapsulated and use the correct method.

之所以在C#编译器提供了自动性能,因为它们是常见的,而且几乎总是以同样的方式,与集合处理不过当你正在发现的情况是很少这么简单 - 有许多不同的方式,正确的方法始终视情况而定,仅举几例:

The reason why the C# compiler provides auto-properties is because they are common and almost always work the same way, however as you are discovering the situation is rarely as simple when dealing with collections - there are many different ways of exposing a collection, the correct method always depends on the situation, to name a few:

通常没有真正的需要放置在暴露的收集任何真正的限制:

Often there is no real need to place any real restrictions on the exposed collection:

public List<T> Collection
{
    get
    {
        return this.collection;
    }
    set
    {
        if (value == null)
        {
            throw new ArgumentNullException();
        }
        this.collection = value;
    }
}
private List<T> collection = new List<T>();

它可以是一个好主意,以确保该集合不能为null,否则,你可以使用自动属性。除非我有一个很好的理由,我想收集更多的封装我总是用这种方法简便。

Its can be a good idea to make sure that the collection is never null, otherwise you can just use auto-properties. Unless I have a good reason for wanting more encapsulation of my collection I always use the this method for simplicity.

您可以code这个任何你喜欢的方式,但这个想法是一样的 - 暴露集合允许项目进行修改,但基本集合本身不能被替换为另一个集合。例如:

You can code this any way you like, but the idea is the same - the exposed collection allows items to be modified but the underlying collection itself cannot be replaced with another collection. For example:

public IList<T> Collection
{
    get
    {
        return this.collection;
    }
}
private ObservableCollection<T> collection = new ObservableCollection<T>();

我倾向于使用这种简单的模式,当事情处理如观察到收藏当消费者应该可以修改该集合,但我已经订阅了更改通知 - 如果让消费者调换整个集合,那么你只会引起头痛

I tend to use this simple pattern when dealing with things like observable collections when the consumer should be able to modify the collection but I've subscribed to change notifications - If you let consumers swap the entire collection then you would just cause headaches.

你经常从修改一个裸露的收集欲prevent消费者 - 通常但是您做要曝光类能够修改集合。一个简单的方法来做到这一点是通过暴露您的收藏只读副本:

Frequently you want to prevent consumers from modifying an exposed collection - usually however you do want the exposing class to be able to modify the collection. An easy way to do this is by exposing a read-only copy of your collection:

public ReadOnlyCollection<T> Collection
{
    get
    {
        return new ReadOnlyCollection<T>(this.collection);
    }
}
private List<T> collection = new List<T>();

这带有返回的集合永远不会改变,即使底层集合改变的财产。这通常是一件好事,因为它使消费者可以遍历返回的集合,而不必担心它可能会更改:

This comes with the property that the returned collection never changes, even if the underlying collection changes. This is often a good thing as it allows consumers to iterate through the returned collection without fear that it might be changed:

foreach (var item in MyClass.Collection)
{
    // This is safe - even if MyClass changes the underlying collection
    // we won't be affected as we are working with a copy
}

然而,这并不总是期望的(或期望的)行为 - 例如 控件属性不以这种方式工作。你也应该考虑以这种方式复制许多大的集合可能是低效的。

However this isn't always the expected (or desired) behaviour - for example the Controls property doesn't work this way. You should also consider that copying many large collections in this way is potentially inefficient.

当为只读总是露出集合知道,在控制项目的可以的还是被修改。同样,这可能是一件好事,但如果你想暴露的收集是完全不可修改,然后确保集合中的项目也只读/不可变的(如 System.String )。

When exposing collections that are read only always be aware that the items in the control can still be modified. Again this might be a good thing, but if you want the exposed collection to be "completely" unmodifiable then make sure that the items in the collection are also read-only / immutable (e.g. System.String).

假设你要公开的项目可以添加到一个集合,但不会被删除?

Suppose you want to expose a collection that items can be added to, but not removed? You could expose properties on the exposing class itself:

public ReadOnlyCollection<T> Collection
{
    get
    {
        return new ReadOnlyCollection<T>(this.collection);
    }
}
private List<T> collection = new List<T>();

public AddItem(T item);

不过,如果你的对象有很多这样的集合,然后你的界面可以迅速得到令人困惑和混乱。此外,我觉得这种模式是有可能违反直觉的次数:

However if your object has many such collections then your interface can quickly get confusing and messy. Also I find this pattern to be potentially counter-intuitive at times:

var collection = MyClass.Collection;
int count = collection.Count;

MyClass.AddItem(item);

Debug.Assert(collection.Count > count, "huh?");

它多了很多努力,但IMO了一个更简洁的方法是揭露一个自定义的集合,它封装了你的真实的收集以及如何收集能和不能改变的规则,例如:

Its a lot more effort, but IMO a neater method is to expose a custom collection that encapsulates your "real" collection and the rules about how the collection can and can't be changed, for example:

public sealed class CustomCollection<T> : IList<T>
{
    private IList<T> wrappedCollection;

    public CustomCollection(IList<T> wrappedCollection)
    {
        if (wrappedCollection == null)
        {
            throw new ArgumentNullException("wrappedCollection");
        }
        this.wrappedCollection = wrappedCollection;
    }

    // "hide" methods that don't make sense by explicitly implementing them and
    // throwing a NotSupportedException
    void IList<T>.RemoveAt(int index)
    {
        throw new NotSupportedException();
    }

    // Implement methods that do make sense by passing the call to the wrapped collection
    public void Add(T item)
    {
        this.wrappedCollection.Add(item);
    }
}

使用示例:

public MyClass()
{
    this.wrappedCollection = new CustomCollection<T>(this.collection)
}

public CustomCollection<T> Collection
{
    get
    {
        return this.wrappedCollection;
    }
}
private CustomCollection<T> wrappedCollection;
private List<T> collection = new List<T>();

裸露收集现​​在encapsualtes如何收集,不能修改我们的规则,也立即反映到底层集合(可能是也可能不是一件好事)所做的更改。它也有潜在​​的大集合更有效。

The exposed collection now encapsualtes our rules on how the collection can and can't be modified and also immediately reflects changes made to the underlying collection (which may or may not be a good thing). Its also potentially more efficient for large collections.