【问题标题】:System.ValueType UnderstandingSystem.ValueType 理解
【发布时间】:2010-08-17 14:34:41
【问题描述】:

我试图创建一个ValueType

我知道创建结构会对我有所帮助。

我还尝试从System.ValueType 派生一个类型,这是一个抽象类。

但我收到了编译器错误消息

".. 不能从特殊类 System.ValueType 派生"

当我看到ValueType 的元数据时,它看起来像是一个常规的抽象类。任何非密封类都应该是可派生的。但是System.ValueType 不是密封类。

  1. 是什么让它特别?

  2. 是 C# 编译器觉得它很特别吗?

  3. 如果是这样,是否建议将其作为编译器设计的规则?我的意思是它是通用语言规范的一部分吗?

【问题讨论】:

  • 要真正了解如何最好地创建值类型,请查看 Bill Wagner 的 Effective C#(第 2 版);尤其是第 20 项。它还有很多其他非常有用的信息。
  • 非常好的和明确的问题。节省阅读时间。我鼓励大家把这个问题写清楚。谢谢。

标签: c# .net-2.0


【解决方案1】:

ValueType 是个善意的谎言。

内置的数值类型(int、long、byte)、char、enums和structs都是值类型。

这意味着它们对对象类型具有不同的标识和等效概念。如果我这样做 x = y 并且 x 和 y 是引用类型,那么 x 和 y 现在指向完全相同的对象。但是,如果我这样做 x = y 并且 x 和 y 是值类型,那么 x 和 y 现在是两个完全不同的对象,它们恰好是相同的。 (这也反映在==Equals 中,尽管可以覆盖)。

(这是人们谈论堆栈和堆的地方,如果他们还没有,那真的是一个实现细节,虽然重要,但不是值和引用类型之间的区别点)。

现在,基本上这一切都很好,但是关于引用类型的一件事是它们都受益于从 System.Object 继承。值类型 int 并不是真的,这也很好,因为它在许多方面都好得多,它只是由非常擅长这样做的可爱的 CPU 指令处理的四个字节的内存。不过,有时将 int 视为继承自 System.Object 有时很有用,您也可以这样做。

当然,这意味着您可以使用 int 执行仅对 System.Object 有意义的操作,因此当您这样做时,int 被“装箱”,然后可以再次“拆箱”。

这很好,但是如果我们想做一些特定于值类型的事情怎么办?更重要的是,如果 CLR 的设计者这样做了(特别是,他们想要一个 GetHashCode 用于与上述基于值的等价相关的值类型,而不是对象所具有的基于身份的等价)?

为此我们有ValueType。系统将所有值类型视为继承自此类,而后者又继承自 Object。枚举依次继承自值类型,所有枚举类型都继承自它,从而允许所有枚举中的一些通用功能。

因此,如果您想处理所有值类型的超类,请使用 ValueType,但如果您想实际创建值类型,请根据需要创建结构或枚举。


通用类型系统解释:

结构是从 System.ValueType 隐式派生的值类型,而 System.ValueType 又从 System.Object 派生。结构对于表示内存需求较小的值以及将值作为按值参数传递给具有强类型参数的方法非常有用。在 .NET Framework 类库中,所有原始数据类型(Boolean、Byte、Char、DateTime、Decimal、Double、Int16、Int32、Int64、SByte、Single、UInt16、UInt32 和 UInt64)都定义为结构。

与类一样,结构定义了数据(结构的字段)和可以对该数据执行的操作(结构的方法)。这意味着您可以调用结构上的方法,包括在 System.Object 和 System.ValueType 类上定义的虚拟方法,以及在值类型本身上定义的任何方法。换句话说,结构可以具有字段、属性和事件,以及静态和非静态方法。您可以创建结构的实例,将它们作为参数传递,将它们存储为局部变量,或者将它们存储在另一个值类型或引用类型的字段中。结构也可以实现接口。

值类型在几个方面也与类不同。首先,虽然它们隐式继承自 System.ValueType,但它们不能直接继承自任何类型。同样,所有值类型都是密封的,这意味着不能从它们派生其他类型。它们也不需要构造函数。

对于每个值类型,公共语言运行时都提供一个对应的装箱类型,这是一个与值类型具有相同状态和行为的类。值类型的实例在传递给接受 System.Object 类型参数的方法时会被装箱。当控制从接受值类型作为按引用参数的方法调用返回时,它被取消装箱(即,从类的实例转换回值类型的实例)。当需要装箱类型时,某些语言要求您使用特殊语法;其他人在需要时会自动使用盒装类型。定义值类型时,您同时定义了装箱和未装箱类型。

ValueType 的奇怪之处在于允许上述情况发生。

【讨论】:

  • 感谢您的解释。但实际上他们是如何让它变得特别的仍然是我的问题。 System.ValueType 不是密封的和抽象的。所以它应该有资格成为父类。 (对于 Enum、Int32 等也是如此。)如果 C# 编译器对它进行特殊处理,它也应该是其他语言(VB.Net、J#)。但我在 Microsoft 公共语言规范中没有看到注释。 msdn.microsoft.com/en-us/library/12a7a7h3.aspx 。所以我还是觉得有什么隐藏的,这有不同的解释。
  • @Saravanandss 添加了上述通用类型系统文档的解释。并不是说它坚持值类型隐式 派生自 System.ValueType,同时还坚持它们不能派生自任何东西。通过您描述的怪异来解决收缩问题。
  • 长得像鸭子,叫起来像鸭子,游起来像鸭子,是鸭子。但事实并非如此。
  • @M.kazemAkhgary 是的,ValueType 不是值类型!
【解决方案2】:

结构是值类型。值类型是特殊的,因为它们分配在堆栈而不是堆上。要从 ValueType “继承”,您必须创建一个结构。

【讨论】:

  • 结构是价值,类型是理论。但我的问题不同,是什么让 System.ValueType 如此特别?它不是密封类,但它不允许派生。
  • @Saravanandss,“结构是一种值类型”是事实,而不是理论。它确实发生了。
【解决方案3】:

无法从 ValueType 派生特定于 C# 编译器。如果我们查看托管 C++ 代码:

value class Foo {};
value class Foo : System::ValueType {};

这两个编译并且是相同的。当然,

ref class Foo : System::ValueType {};

将给出错误 C3050:引用类不能从 'System::ValueType' 继承。
不确定其他编译器允许什么。

如果你想从 C# 中的 ValueType 派生,使用 struct 而不是 class,编译器会处理它。

【讨论】:

    【解决方案4】:

    C# 不允许值类型从其他类继承。值类型(结构)可以实现接口。

    【讨论】:

      【解决方案5】:

      你不能直接继承 ValueType。所有值类型都隐式地从 ValueType 派生。与引用类型不同,您不能从值类型派生新类型。但是,与引用类型一样,结构也可以实现接口。

      MSDN了解更多

      【讨论】:

      • 任何非密封类都应该是可派生的。但 System.ValueType 不是密封类。它自称是一个特殊的类。是什么让它特别?是我的问题
      • @Saravanandss CLR .NET 给它特别的剂量。
      【解决方案6】:
      1. “类”扩展“System.Object”,“结构”扩展“System.ValueType”。如果你可以让一个类扩展'System.ValueType',那么'struct'关键字就没有多大意义了。

      2. 是的。应该只有一种方法可以完成任何给定的事情。一旦提供了两种做某事的方法,您就使一切变得更加复杂。一般规则。

      【讨论】:

      • 需要记住,.NET 中的一切最终都是 System.Object。
      • 我不同意这一点。在编程世界中,有很多方法可以做事。例如,如果 (a == b) c = 0;否则 c = 1;相当于 c = (a == b) ? 0:1;并且 Nullable s = null;整数32? s = 空;是平等的。没有规则说应该只有一种方法来完成一件事。
      • 应该总是有尽可能多的方法来做某事。为您完成事情的方式应该尽可能少。这两个免费开发者。
      • @Michael:是的,是吗?我没有看到相关性。 @Sara:问题是关于编译器设计,而不是编程语言设计。两者是不同的。 @Jon:确实,当您设计编译器时,只有一种方法可以做某事,那么您只有一种方法可以让编译器为您完成这件事。
      • 不是针对你,而是针对新手,因为他们可能不认为值类型最终仍然是对象类型。
      【解决方案7】:

      这让我感到困惑... Enum 是从 ValueType 派生的。这让我觉得上面只是一个编译器限制。

      [Serializable, ComVisible(true)]
      public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
      {
        ...
      }
      

      【讨论】:

      • 在某些情况下,运行时会根据它们是否派生自 ValueType 来区别对待类型。如果编译器允许创建一个从ValueType 派生的类并且不包含任何代码,那么这样的类的行为就像一个结构。如果该类确实包含代码,则此类代码可能不起作用,因为结构和类以不同的方式访问this;如果编译器不知道this 是一个结构,它会生成错误的代码来访问它。
      猜你喜欢
      • 2017-09-19
      • 1970-01-01
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      相关资源
      最近更新 更多