在.net中可以将数组或列表作为属性返回吗?
本文关键字:属性 返回 列表 net 数组 | 更新日期: 2023-09-27 18:05:25
我在MSDN上读了一些关于应该做什么和不应该做什么的文档,关于是否应该作为属性或方法来实现。我遇到了一个特别的规则,我有一个问题。
如果"操作返回一个数组"使用一个方法(而不是一个属性)。
页面在这里:选择属性和方法
使用一个方法,该方法的操作返回一个数组,因为要保留内部数组,就必须返回对象使用的数组的引用,而不是数组的深层副本财产。这个事实,结合了开发人员使用的事实属性就像字段一样,会导致效率非常低代码。
我理解属性的get方法将返回对数组的引用,这将允许数组被更改,即使没有设置。在他们给出的例子中,每次访问属性时,他们都会对数组进行深度复制,我猜是为了避免这种情况的发生,而这反过来又非常低效。
如果属性只是返回引用,而不做所有的复制,这不会是低效的,对吗?而且使用方法而不是属性不会自动保护列表不被修改。这几乎是相同的场景,你仍然需要一个深度拷贝。
使用属性,只是返回对数组的引用总是不好的做法?如果您希望调用者能够修改数组,或者您不关心他们是否修改了数组,该怎么办?它仍然很糟糕吗?为什么?如果是这样,允许调用者修改的正确方法是什么?
允许调用者通过属性修改内部数组吗?是的,当然,但你将承担一系列可能的问题。你如何处理这些问题以及你能忍受什么取决于你自己。
MSDN建议在严格意义上是正确的。也就是说,我以前看到过List<T>
和T[]
属性从课堂上返回。如果你的类是一个非常简单的POCO,这不是一个大问题,因为这些类只是原始数据,没有真正的业务逻辑要影响。
也就是说,如果我返回一个列表,并且我不希望任何人扰乱内部列表,我要么每次返回一个深度拷贝,要么返回一个ReadOnlyCollection
,要么返回一个迭代器。例如,有很多地方我缓存web服务请求调用,当我返回一个缓存项时,我不希望调用者修改数据,否则他们会修改我缓存的内容。因此,我在那里进行深度复制(这仍然比web服务调用的开销快)。
你只需要知道你的使用是否需要安全。类是否仅供内部使用?或者它是为更广泛的受众而设计的,你不知道他们会用它做什么?这类问题可能会影响你的回答。
很抱歉给出一个"视情况而定"的答案,但这确实取决于你的目标是什么,以及类的内部是否对变化敏感。
UPDATE你也可以返回一个迭代器,我避免返回IEnumerable作为一个超类向上强制转换,因为它可以向下强制转换,但如果你返回一个迭代器(比如使用Skip(0)),你是安全的(当然,除了仍然能够修改包含的对象)。
例如:public IEnumerable<T> SomeList
{
get { return _internalList.Skip(0); }
}
优于:
public IEnumerable<T> SomeList
{
get { return _internalList; }
}
因为后者仍然可以被强制转换回List<T>
或其他类型,而前者是迭代器,不能修改。
如果您的类的内部状态不依赖于您公开的数组或列表,则可以按原样返回。因此,不应该通过更改列表或数组来破坏类。
如果你信任调用代码(即它是由你写的),如果它能提高你的性能(可测量的和与你的应用相关的),它也可以。
如果你想给只读访问,你可以返回一个ReadonlyCollection
,它包围了你想要公开的列表或数组。
这几乎总是一件坏事,但它并不能阻止人们这样做(包括我)。这是一个为使用场景使用合适的案例的问题。
然而,要保护数组的封装,最简单的方法是只公开一个IEnumerable或ICollection,以及改变内部数组的方法。参见下面的示例。我在这里使用List,因为语义更简单。class YourClass {
List<string> list = new List<string>();
public IEnumerable<string> List {
get { return list; }
}
public void Add(string str) {
list.Add(str);
}
}
由于返回类型是IEnumerable/ICollection,所以除了通过类的方法/属性之外,不可能改变内部状态。
最终的封装是在属性访问时复制整个集合,但这在大多数情况下是低效的。
从语义上讲,没有太多代价的、幂等且有效的事物应该被赋予一个属性。改变数据或具有高执行成本的东西应该是一个方法。
您的假设是正确的,如果您只是从属性get返回一个现有数组,而不是执行复制,则不再是效率问题,因为这样做与返回任何其他引用类型一样有效。
,它是一个"糟糕的实践",这很大程度上源于他们描述的原因:即使你只提供一个访问你的财产,用户仍然可以修改数组的内容,从而改变您的类的状态,没有你的课有一种验证方式的变化,最糟糕的是,这个设计的结果不是很明显,除非你已经有了一个足够深的c#数组语义知识。
如果您可以接受用户将能够改变数组的内容,并且如果您可以接受您的类将没有资源来验证或阻止这些更改,那么返回数组引用可能是可以接受的,只是要确保记录该决定及其背后的理由。
但是,除非您确实需要原始数组的性能,否则您最好还是让属性返回一个集合类实例。类的消费者仍然能够使用相同的语法通过索引引用项,但保留了使返回的集合不可变或提供更新验证的功能。一般来说,使用属性返回一个数组或集合并不是一个坏的做法。特别是如果每次都返回相同的缓存实例。但是,如果每次都创建一个新实例,那么称为CreateItems()之类的方法将使这一事实更清楚。
我不同意你想要to preserve the internal array, you would have to return a deep copy of the array, not a reference to the array used by the property.
事实上,如果一个属性确实返回一个深度拷贝,那将是相当"惊人"的。
有时你确实想保留内部结构,只是返回它的只读视图(以IEnumerable<T>
的形式),但这与属性是正交的。
它们使外界认为属性应该像字段一样,并且字段允许修改。如果他们说数组/列表作为字段是不好的做法,那是完全不同的事情,与属性无关。