1. ホーム
  2. .net

[解決済み] なぜ構造体は継承をサポートしないのですか?

2022-06-06 17:20:56

疑問点

.NETの構造体が継承をサポートしていないことは知っていますが、正確にはわかりません。 なぜ がこのように制限されている理由ははっきりしません。

構造体が他の構造体を継承できない技術的な理由は何ですか?

どのように解決するのですか?

値型が継承をサポートできないのは、配列のせいです。

問題は、パフォーマンスとGCの理由から、value型の配列は"inline"で保存されることです。 例えば、与えられた new FooType[10] {...} がある場合、もし FooType が参照型である場合、11個のオブジェクトが管理ヒープ上に作成されます(配列に1個、各型のインスタンスに10個)。 もし FooType が値型である場合、管理されたヒープ上に1つのインスタンスのみが作成されます -- 配列そのものに対してです (各配列値は配列と共に "inline"で保存されるからです)。

さて、値型による継承があったとします。 上記の配列の "インライン ストレージ" の動作と組み合わせると、次のように悪いことが起こります。 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);

通常の変換ルールでは Derived[] への変換は Base[] (良くも悪くも)ですから、上の例でs/struct/class/gとすれば、何の問題もなく、期待通りにコンパイルされ実行されます。しかし、もし BaseDerived が値の型であり、配列がインラインで値を格納する場合、問題が発生します。

問題があるのは 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 !

デバッグしてみる その !