【问题标题】:Is there any advantage to using generic collections when you are only storing reference types?当您只存储引用类型时,使用泛型集合有什么好处吗?
【发布时间】:2011-03-25 22:13:48
【问题描述】:

我一直在读一本书并遇到一个声明,即泛型允许程序员在使用例如时避免装箱和拆箱。 Stack

我认为这并不完全正确,因为堆栈(不是通用的)可以只保存引用类型变量,然后只进行强制转换,没有装箱。

编辑:AFAIK,装箱涉及 值类型到对象转换。 这是正确的还是我错过了什么?

【问题讨论】:

  • 您是指Stack 类还是Stack<> 泛型类?它们是不同的。
  • 是的,没错。只有在处理值类型时,装箱才有意义。到目前为止,大多数答案都没有抓住重点。
  • 用一个具体的例子来详细说明 Cody Gray 的评论:如果你将一个字符串存储在一个非泛型集合中,该集合存储一个对该字符串的引用,并且该引用是 object 类型的。 (所有引用类型都是如此,而不仅仅是字符串。)这与装箱不同。装箱涉及创建一个包含值类型值副本的对象实例,目的是用作对象引用的目标(在这种情况下,集合将持有)。

标签: .net boxing generics unboxing


【解决方案1】:

如果您有Stack<SomeReferenceType>,则不会发生(取消)装箱,因为引用永远不需要装箱。引用是类型在 .NET 中工作的“正常”方式 - object 毕竟是引用类型。

编辑:您澄清的问题清楚地表明您在谈论Stack 的非泛型变体。你说这个变体不需要装箱和拆箱引用类型是正确的,因为引用类型永远不会被装箱(并且确实不能被装箱)。同样正确的是,如果您将object 的子类的一个实例放入Stack,那么当您取出该项目并尝试使用该实例的某个方面时(这并不适用于所有objects ),那么您将需要强制转换为子类。

请注意(对于引用类型)虽然我们从概念上讲“强制转换”为object,但这并不对应于实际操作;向上转型(对象或任何其他超类)是免费的。机器需要在向下转换中验证向下转换是否有效。因此,仅用引用类型的实例填充Stack 的非泛型变体是有效的;仅在从Stack and 转换为更具体的类型时才会发生开销。

为什么你应该更喜欢泛型集合。

虽然(向下)投射相当便宜,但它不是免费的。即使对于引用类型,如果您需要再次提取值,预计泛型集合的性能会略微优于它们的非泛型集合。但是,即使您(像往常一样)对几个 CPU 周期不感兴趣,通用集合更可取,因为它们可以帮助您更快地发现错误:如果您不小心将错误类型的项目添加到一个非通用列表,您不会收到警告,也不会看到错误,直到您提取并使用该值,这可能会更晚(甚至永远不会)并且在不同的代码文件中甚至不同的组装。调试此类错误确实很麻烦,特别是如果您的代码有一堆层并且错误不在直接访问集合的层中。

相比之下,如果您尝试添加不正确类型的值,则通用集合甚至不会编译。即使您需要实现类型不安全的接口并且编译器无法捕获错误,您仍然具有将项目添加到收集(接近错误的根本原因)而不是提取后(远离错误的根本原因)。代码中会有错误,您希望快速、廉价地找到它们,而不是在看似无关的代码中出现奇怪的异常。

真的,曾经使用非泛型集合的唯一原因是为了与需要使用它们的遗留 API 进行互操作。

【讨论】:

  • “请注意(对于引用类型)...向上转换(到对象或任何其他超类)是免费的。” vs. “虽然转换相当便宜,它不是免费的。即使对于引用类型..."您可能需要澄清一下。
  • 是的,这很令人困惑 - 已修复。
【解决方案2】:

问题在于Stack 类将您放入其中的对象存储为object。这意味着,每次您将某些东西放入堆栈时,它都会被装箱到一个对象中,并且每次您想在堆栈中的对象上使用时,您都需要再次将其拆箱,以获得正确的类型。

【讨论】:

  • 问题是装箱过程只涉及值类型。
  • 我认为他可能在谈论通用的Stack<> 类。
  • @Mocco:是的,你是对的。但是,是什么让您认为,您不能在堆栈中使用值类型?也许我误解了你的问题......
  • 谢谢。我的问题是这本书是否正确地表明每次将对象添加到 Stack 类时都会发生装箱。我认为这是错误的,因为它只发生在值类型上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-22
  • 2010-09-09
  • 1970-01-01
  • 2010-11-05
  • 2010-12-13
相关资源
最近更新 更多