.NETの構造体は継承をサポートしていないことは知っていますが、正確にはわかりません。なぜこのように制限されています。
構造体が他の構造体から継承できない技術的な理由は何ですか?
ベストアンサー1
値型が継承をサポートできない理由は、配列のためです。
問題は、パフォーマンスと GC の理由から、値型の配列が「インライン」で保存されることです。たとえば、 が参照型のnew FooType[10] {...}
場合FooType
、マネージド ヒープには 11 個のオブジェクトが作成されます (配列用に 1 つ、各型インスタンス用に 10 個)。 がFooType
値型の場合、マネージド ヒープには配列自体のインスタンスが 1 つだけ作成されます (各配列値は配列とともに「インライン」で保存されるため)。
さて、値型による継承があったとします。配列の上記の「インラインストレージ」動作と組み合わせると、次のような悪い事態が発生します。C++で。
次の疑似 C# コードを検討してください。
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);
通常の変換ルールでは、 aDerived[]
は a に変換可能ですBase[]
(良くも悪くも)。そのため、上記の例で s/struct/class/g を実行すると、問題なく期待どおりにコンパイルおよび実行されます。ただし、Base
と がDerived
値型であり、配列が値をインラインで格納する場合は、問題が発生します。
Square()
は について何も知らないためDerived
、ポインタ演算のみを使用して配列の各要素にアクセスし、定数 ( ) ずつ増分するため、問題が発生しますsizeof(A)
。アセンブリは、おおよそ次のようになります。
for (int i = 0; i < values.Length; ++i)
{
A* value = (A*) (((char*) values) + i * sizeof(A));
value->A *= 2;
}
(はい、これはひどいアセンブリですが、ポイントは、派生型が使用されていることをまったく認識せずに、既知のコンパイル時定数で配列を増分することです。)
したがって、これが実際に起こった場合、メモリ破損の問題が発生します。具体的にはSquare()
、values[1].A*=2
実は変更中values[0].B
!
デバッグしてみるそれ!