.net 为什么不';t结构是否支持继承?

.net 为什么不';t结构是否支持继承?,.net,inheritance,struct,.net,Inheritance,Struct,我知道.NET中的结构不支持继承,但不清楚为什么它们会以这种方式受到限制 是什么技术原因阻止结构从其他结构继承?以下是您的看法: 结构对于具有值语义的小型数据结构特别有用。复数、坐标系中的点或字典中的键值对都是结构的好例子。这些数据结构的关键在于,它们的数据成员很少,不需要使用继承或引用标识,并且可以使用值语义方便地实现它们,其中赋值复制值而不是引用 基本上,它们应该保存简单的数据,因此没有诸如继承之类的“额外特性”。从技术上讲,它们可能支持某种有限类型的继承(不是多态性,因为它们在堆栈上),但

我知道.NET中的结构不支持继承,但不清楚为什么它们会以这种方式受到限制

是什么技术原因阻止结构从其他结构继承?

以下是您的看法:

结构对于具有值语义的小型数据结构特别有用。复数、坐标系中的点或字典中的键值对都是结构的好例子。这些数据结构的关键在于,它们的数据成员很少,不需要使用继承或引用标识,并且可以使用值语义方便地实现它们,其中赋值复制值而不是引用

基本上,它们应该保存简单的数据,因此没有诸如继承之类的“额外特性”。从技术上讲,它们可能支持某种有限类型的继承(不是多态性,因为它们在堆栈上),但我认为不支持继承也是一种设计选择(就像.NET语言中的许多其他东西一样)


另一方面,我同意继承的好处,并且我认为我们都已经达到了希望我们的
struct
从另一个继承的地步,并且意识到这是不可能的。但在这一点上,数据结构可能非常高级,无论如何它都应该是一个类。

结构不使用引用(除非它们被装箱,但您应该尽量避免),因此多态性没有意义,因为没有通过引用指针进行间接寻址。对象通常位于堆上,并通过引用指针引用,但结构是在堆栈上分配的(除非它们已装箱),或者是在堆上引用类型占用的内存“内部”分配的。

类继承是不可能的,因为结构直接放置在堆栈上。继承结构将比其父结构大,但JIT不知道这一点,并试图在太少的空间上放置太多的内容。听起来有点不清楚,让我们写一个例子:

struct A {
    int property;
} // sizeof A == sizeof int

struct B : A {
    int childproperty;
} // sizeof B == sizeof int * 2
如果可能,它将在以下代码段上崩溃:

void DoSomething(A arg){};

...

B b;
DoSomething(b);

空间分配给A的大小,而不是B的大小。

Imagine structs支持的继承。然后宣布:

BaseStruct a;
InheritedStruct b; //inherits from BaseStruct, added fields, etc.

a = b; //?? expand size during assignment?
这意味着结构变量没有固定的大小,这就是为什么我们有引用类型

更好的是,考虑这一点:

BaseStruct[] baseArray = new BaseStruct[1000];

baseArray[500] = new InheritedStruct(); //?? morph/resize the array?

这似乎是一个非常常见的问题。我想补充一点,值类型存储在声明变量的“原位”;除了实现细节之外,这意味着没有表示对象的对象头,只有变量知道驻留在那里的数据类型。

有一点我想纠正。尽管结构不能被继承的原因是因为它们位于堆栈上,但这是正确的,但这是一个半正确的解释。与任何其他值类型一样,结构也可以存在于堆栈中。因为这将取决于变量的声明位置,所以它们要么存在于堆栈中,要么存在于堆中。这将是当它们分别是局部变量或实例字段时

塞西尔这样说是正确的


我想强调的是,值类型可以存在于堆栈上。这并不意味着他们总是这样做。局部变量(包括方法参数)将被删除。所有其他人都不会。尽管如此,这仍然是他们无法继承的原因。:-)

IL是一种基于堆栈的语言,因此使用参数调用方法的过程如下:

  • 将参数推到堆栈上
  • 调用该方法
  • 当该方法运行时,它会从堆栈中弹出一些字节以获取其参数。它确切地知道要弹出多少字节,因为参数要么是引用类型指针(32位上总是4个字节),要么是一个值类型,其大小总是确切地知道

    如果它是引用类型指针,那么该方法将在堆中查找对象并获取其类型句柄,该句柄指向一个方法表,该方法表为该确切类型处理该特定方法。如果是值类型,则不需要查找方法表,因为值类型不支持继承,因此只有一种可能的方法/类型组合


    如果值类型支持继承,那么会有额外的开销,因为结构的特定类型必须与其值一起放在堆栈上,这意味着对类型的特定具体实例进行某种方法表查找。这将消除值类型的速度和效率优势。

    结构确实支持接口,因此可以通过这种方式执行一些多态操作。

    值类型不支持继承的原因是数组

    问题在于,出于性能和GC原因,值类型的数组存储为“内联”。例如,给定
    newfootype[10]{…}
    ,如果
    FooType
    是引用类型,则将在托管堆上创建11个对象(一个用于数组,10个用于每个类型实例)。如果
    FooType
    改为值类型,那么在托管堆上只会为数组本身创建一个实例(因为每个数组值都将与数组“内联”存储)

    现在,假设我们有值类型的继承。当与上述阵列的“内联存储”行为相结合时,就会发生不好的事情,这一点可以看出

    考虑一下这个伪C代码:

    (是的,这是一个糟糕的程序集,但关键是我们将以已知的编译时常量递增数组,而不知道正在使用派生类型。)

    所以,如果这真的发生了,我们会有内存损坏问题。具体来说,在
    Square()
    中,
    值[1]。A*=2
    实际上将修改
    值[0]。B


    试着调试一下

    结构在堆栈上分配。这意味着值semanti
    struct Base
    {
        public int A;
    }
    
    struct Derived : Base
    {
        public int B;
    }
    
    void Square(Base[] values)
    {
      for (int i = 0; i < values.Length; ++i)
          values [i].A *= 2;
    }
    
    Derived[] v = new Derived[2];
    Square (v);
    
    for (int i = 0; i < values.Length; ++i)
    {
        A* value = (A*) (((char*) values) + i * sizeof(A));
        value->A *= 2;
    }
    
    struct A
    {
        public int Integer1;
    }
    
    struct B : A
    {
        public int Integer2;
    }
    
    A a = new B();