.net 为什么不';t结构是否支持继承?
我知道.NET中的结构不支持继承,但不清楚为什么它们会以这种方式受到限制 是什么技术原因阻止结构从其他结构继承?以下是您的看法: 结构对于具有值语义的小型数据结构特别有用。复数、坐标系中的点或字典中的键值对都是结构的好例子。这些数据结构的关键在于,它们的数据成员很少,不需要使用继承或引用标识,并且可以使用值语义方便地实现它们,其中赋值复制值而不是引用 基本上,它们应该保存简单的数据,因此没有诸如继承之类的“额外特性”。从技术上讲,它们可能支持某种有限类型的继承(不是多态性,因为它们在堆栈上),但我认为不支持继承也是一种设计选择(就像.NET语言中的许多其他东西一样).net 为什么不';t结构是否支持继承?,.net,inheritance,struct,.net,Inheritance,Struct,我知道.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是一种基于堆栈的语言,因此使用参数调用方法的过程如下:
如果值类型支持继承,那么会有额外的开销,因为结构的特定类型必须与其值一起放在堆栈上,这意味着对类型的特定具体实例进行某种方法表查找。这将消除值类型的速度和效率优势。结构确实支持接口,因此可以通过这种方式执行一些多态操作。值类型不支持继承的原因是数组 问题在于,出于性能和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();