为什么结构不支持继承?
我知道 .NET 中的结构不支持继承,但不清楚为什么它们以这种方式受到限制.
I know that structs in .NET do not support inheritance, but its not exactly clear why they are limited in this way.
什么技术原因阻止结构从其他结构继承?
What technical reason prevents structs from inheriting from other structs?
值类型不支持继承的原因是数组.
The reason value types can't support inheritance is because of arrays.
问题在于,出于性能和 GC 的原因,值类型的数组是内联"存储的.例如,给定 new FooType[10] {...}
,如果 FooType
是引用类型,则会在托管堆上创建 11 个对象(一个用于数组, 每个类型实例 10 个).如果 FooType
是值类型,则只会在托管堆上创建一个实例——用于数组本身(因为每个数组值都将与数组内联"存储).
The problem is that, for performance and GC reasons, arrays of value types are stored "inline". For example, given new FooType[10] {...}
, if FooType
is a reference type, 11 objects will be created on the managed heap (one for the array, and 10 for each type instance). If FooType
is instead a value type, only one instance will be created on the managed heap -- for the array itself (as each array value will be stored "inline" with the array).
现在,假设我们有值类型的继承.当与数组的上述内联存储"行为结合时,会发生坏事,正如可以看到的 在 C++ 中.
Now, suppose we had inheritance with value types. When combined with the above "inline storage" behavior of arrays, Bad Things happen, as can be seen in C++.
考虑这个伪 C# 代码:
Consider this pseudo-C# code:
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);
按照正常的转换规则,Derived[]
可以转换为 Base[]
(无论好坏),所以如果你 s/struct/class/g对于上面的例子,它会按预期编译和运行,没有问题.但是如果 Base
和 Derived
是值类型,并且数组内联存储值,那么我们就有问题了.
By normal conversion rules, a Derived[]
is convertible to a Base[]
(for better or worse), so if you s/struct/class/g for the above example, it'll compile and run as expected, with no problems. But if Base
and Derived
are value types, and arrays store values inline, then we have a problem.
我们有一个问题,因为 Square()
对 Derived
一无所知,它只会使用指针算术来访问数组的每个元素,递增一个常数 (sizeof(A)
).程序集大概是这样的:
We have a problem because Square()
doesn't know anything about Derived
, it'll use only pointer arithmetic to access each element of the array, incrementing by a constant amount (sizeof(A)
). The assembly would be vaguely like:
for (int i = 0; i < values.Length; ++i)
{
A* value = (A*) (((char*) values) + i * sizeof(A));
value->A *= 2;
}
(是的,这是一个令人讨厌的程序集,但关键是我们将以已知的编译时常量在数组中递增,而不知道正在使用派生类型.)
(Yes, that's abominable assembly, but the point is that we'll increment through the array at known compile-time constants, without any knowledge that a derived type is being used.)
所以,如果这真的发生了,我们就会遇到内存损坏问题.具体来说,在 Square()
中,values[1].A*=2
将实际上修改 values[0].B
!
So, if this actually happened, we'd have memory corruption issues. Specifically, within Square()
, values[1].A*=2
would actually be modifying values[0].B
!
尝试调试那个!