在 C# 中返回 IList<T> vs Array?

Posted

技术标签:

【中文标题】在 C# 中返回 IList<T> vs Array?【英文标题】:returning IList<T> vs Array in C#? 【发布时间】:2010-10-23 09:17:52 【问题描述】:

我最近问某人为什么他更喜欢返回强类型数组而不是 IList。我一直认为,面对一个生命周期很长的项目时,针对接口编程是最灵活、最好的编程方式。所以当他回答时,我感到很奇怪:

我们通常更喜欢不可变类型 超过可变的。数组是 不可变的。 IList 不是。

我不完全确定我是否理解此声明。谁能帮忙澄清一下?

感谢你们提供的任何帮助。

【问题讨论】:

【参考方案1】:

无论“他”是谁,在这个话题上都是 100% 错误的。数组是一个非常可变的。这实际上是不返回数组的原因之一。没有办法阻止调用者将数组的元素更改为他们喜欢的任何内容。

数组不可变的唯一方式是它的长度。一旦分配了一个数组,它的长度就不能改变。即使是 Array.Resize 等 API 实际上也不会调整数组的大小,它们只是分配一个新数组,复制内容并返回新数组(在本例中通过引用)。

不过,我同意在很多情况下最好返回不可变数据。第一个是它允许您返回对类的内部集合的引用,而无需进行完整的复制,同时防止调用者弄乱您的内部状态。大多数可变集合不能做出这样的保证。

【讨论】:

“在其中有效..” - 你的意思是“在哪个更好”? 好吧,我不会走那么远......“他”是一个非常了不起的编码器,但我认为他可能指的是下面其中一个 cmets 的某些方面;但是,我看不到这“购买”了我或我的程序。如果我使用存储库模式返回对象列表,为什么它必须是数组而不是 IList @mkelley33,你在说哪一部分?他肯定错了,无论技能如何,阵列都是不可变的。它们很简单是可变结构。这并没有使他不再是一个编码员。每个人都容易犯错误(我自己尝试每天犯 2 或 3 次),这是我见过很多人犯的错误。我当然会主张通过数组或更好的实际不可变结构返回 IList。随着时间的推移,它只会为您提供更大的灵活性。 哦,我不认为他是完美的,但我想我可能已经断章取义地引用了他,尽管我仍在努力理解上下文。所以我有一个 IRepository,它的 GetAll 方法返回 IList。随着时间的推移,不变性如何为您提供更大的灵活性?感谢所有的帮助。 @mkelley,我的意思更多的是,将接口返回到可变或不可变集合会随着时间的推移为您提供更大的灵活性。【参考方案2】:

我认为他可能认为既然数组的长度属性是不可变的,那么数组在某种程度上比 IList更多不可变,或者他使用了错误的词并将具体与不可变互换。谁知道,但这是一个奇怪的答案。

我认为返回一个 List 有一些暗示可以修改它,或者它可能会在返回一个数组时发生变化并不意味着那么多。

例如,如果您在存储库顶部有一个对象模型,并且有一个像 GetCars() 这样返回 List 的方法,而初级程序员看到了 cars.Add(Car c) ...您会认为他完全疯狂思考汽车。Add(new Car()) 可能实际上将汽车添加到存储库中?数组本质上更明确。

我认为 List 的使用在属性中更合适,比如 Page.Controls.Add

出于几个原因,我更喜欢返回数组而不是 List。

习惯。 1.0/1.1 中的集合 SUCKED

我更喜欢我的方法尽可能返回最简单和最轻量级的对象。如果我需要将 Array 设为 List,这很简单。

它们可以在 .net 1.1 中使用,如果我需要支持旧版本的运行时,它可以减少重构的表面,我可以至少重用我的一些代码或应用相同的对象模型。

【讨论】:

这给了我一直在寻找的“啊哈”时刻!从存储库返回 T[] 的“泄漏”较少(如泄漏抽象法则)。如果我想在存储库中添加一些东西,我会称之为 Add/Insert 方法,对吗?因此,对我来说,假设在返回的列表中添加一些内容将能够按预期参与工作单元或工作,这将适得其反/违反直觉。使用 IRepository 时,他是 100% 正确的!尽管他给我的解释更符合您所说的:Array 在某些方面比 IList 更不可变。非常感谢! +1 对于看到 List.Add 的初级人员可能会认为这是一个好主意(相对于没有看到它 w/ Array)【参考方案3】:

我总是更喜欢ReadOnlyCollection。列表的所有优点,但只读。

【讨论】:

【参考方案4】:

他可能的意思是 IList 包含 Insert、Remove 和 Add 方法,因此可以修改集合本身。另一方面,T[] 不能添加元素。

我应该补充一点,FxCop 建议改为返回 ReadOnlyCollection。索引器是只读的,所以不能改变元素,Add等方法都会抛出NotSupportedException。

【讨论】:

酷!那太棒了。我在工作中不时使用 FxCop,我一直想知道其中的一些建议。感谢您的澄清!【参考方案5】:

原则上他是对的,但他不知道如何正确练习......

我们通常更喜欢不可变类型 超过可变的。

没错。不可变类型更适合使用

数组是不可变的。 IList 不是。

这是不正确的。这些都不是一成不变的。

如果要返回不可变的集合,请返回 IEnumerable&lt;T&gt;ReadOnlyCollection&lt;T&gt;(使用 List&lt;T&gt;.AsReadOnly 方法)。如果它们本身不是不可变的,它们仍然不能保护对象。尽管您只能从集合中读取,但如果它们允许,您仍然可以更改每个对象中的数据。

此外,您还应该考虑所归还的收藏品的“所有权”。如果您创建数组的唯一目的是返回它,那么没有理由不完全控制它。另一方面,如果您要返回一个属于该类成员的集合,则应该只允许尽可能少地访问它。

【讨论】:

【参考方案6】:

我也不明白。数组是非常可变的,除了它们的大小。仍然可以修改单个元素。也许他的意思是数组是值类型而列表是引用类型。我不知道。

无论如何,您应该看看Eric Lippert's opinion 的主题。也可以为你提供一些弹药供讨论。

【讨论】:

谢谢。我读过那篇文章,因此感到困惑哈哈。鉴于对可变性(什么是什么和不是什么)的不同意见,我必须按照我的直觉并返回 IList 直到证明不是这样。我只想说,我完全感谢在这个问题的描述中提供报价的人。 “He-who-must-not-be-name-out-of-respect”是一个了不起的编码员和灵感,但我不会盲目跟随,所以我必须等到我听到他对这里提供的论点的反驳!再次感谢。 独立思考,好! :) 祝你好运说服你的朋友。【参考方案7】:

也许这个人不知道他在说什么?

【讨论】:

我对此表示怀疑。更有可能是我不知道“他”在说什么哈哈。【参考方案8】:

使用接口作为返回类型而不是实际类型本身的原因是对调用者隐藏内部实现。这允许实现更改实际类型,而不会重复应用程序的其余部分。

将内部使用的集合复制到数组中以供外部使用没有任何意义,除了可变或不可变之外没有其他原因。

单独的问题是: - 是否返回强类型数据列表。 IList 或 IList。 始终首选使用强类型数据。 - 可变或不可变。 ICollection、IList 或 IEnumerator 返回您想要允许的数据。对于只读列表,仅返回一个 IEnumerator。如果允许调用者修改集合,请使用 ICollection 或 IList。

【讨论】:

【参考方案9】:

基本上,他的意思是“我们更喜欢在运行时不易更改的结构,因为我们认为它们不易出错。”在这种情况下这是否正确是另一个问题。

【讨论】:

嗯。我喜欢这样,但不知道它是否比针对 IList 编程更有价值,因为我在实现方面具有更大的灵活性。谢谢您的帮助。这让他的立场更加明确。【参考方案10】:

也许他的意思是数组的大小是“不可变的”? 基本上,您声明一次大小,然后就被卡住了。对于列表,您始终可以使用“添加”。

假设如果确定关于列表的大小,也许数组更快一点?

【讨论】:

这正是我的假设,但我真的不知道在使用存储库模式时这会给我带来什么。谢谢!【参考方案11】:

嗯。 IList 是可变的,因为您可以添加和删除项目,而不仅仅是更改已经存在的项目。数组可以让您处理单个项目,但特定索引永远不会变得完全无效。有很多理由更喜欢不变性 - 例如,它使意外数据损坏的表面积小得多。

【讨论】:

使用ICollection,如果你返回一个,返回一个只读。数组实现了这个接口,因此可以选择将接口或列表传递给这样的方法。计数、添加和删除是在通用 ICollection 接口上定义的。

以上是关于在 C# 中返回 IList<T> vs Array?的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的数组如何部分实现 IList<T>?

数组如何实现 IList<T> 而无需在 C# 中实现属性“Count”? [复制]

C#利用GridView对IList<T>或List<T>进行插入、删除和修改

C#

C# List<T;用法

c#中LIST的应用实例谁给个加注释的!灰常感谢!