54

次のような自動プロパティを使用して C# で構造体を定義するとします。

public struct Address
{
    public Address(string line1, string line2, string city, string state, string zip)
    {
        Line1 = line1;
        Line2 = line2;
        City = city;
        State = state;
        Zip = zip;
    }

    public string Line1 { get; protected set; }
    public string Line2 { get; protected set; }
    public string City { get; protected set; }
    public string State { get; protected set; }
    public string Zip { get; protected set; }
}

ファイルをビルドしようとすると、コンパイル エラーが発生しますThe 'this' object cannot be used before all of its fields are assigned to。これは、コンストラクターを変更して、次のようにデフォルトのコンストラクターへの連鎖呼び出しを行うことで解決できます。

public Address(string line1, string line2, string city, string state, string zip): this()
{
    Line1 = line1;
    Line2 = line2;
    City = city;
    State = state;
    Zip = zip;
}

私の質問は、なぜこれが機能するのか、何が起こっているのかということです。私は推測を持っており、IL を見てそれを証明しようとしましたが、IL を分解できると思うかどうかは冗談です。しかし、自動プロパティは、コンパイラが舞台裏でプロパティのフィールドを生成することで機能すると思います。これらのフィールドにはコードからアクセスすることはできません。すべての設定と取得は、プロパティを通じて行う必要があります。構造体を作成するとき、既定のコンストラクターを明示的に定義することはできません。そのため、舞台裏では、開発者が見ることができないフィールドの値を設定するデフォルトのコンストラクターをコンパイラーが生成している必要があります。

すべての IL ウィザードは、私の理論を証明または反証することを歓迎します。

4

2 に答える 2

53

注: C# 6 の時点では、これは必須ではありませんが、C# 6 では読み取り専用の自動実装プロパティを使用する必要があります...

this()コンパイラに関する限り、フィールドが確実に割り当てられていることを確認します。すべてのフィールドをデフォルト値に設定します。プロパティへのアクセスを開始するには、完全に構築された構造体が必要です

面倒ですが、その通りです。これを本当に構造体にしたいですか?また、(派生できない) 構造体で保護されたセッターを使用するのはなぜですか?

于 2008-11-07T14:10:33.207 に答える
0

Getプロパティは、メソッドおよび/またはメソッドのカプセル化にすぎませんSet。CLRには、特定のメソッドをプロパティと見なす必要があることを示すメタデータがあります。つまり、コンパイラーは、メソッドでは許可されないいくつかの構造を許可する必要があります。たとえば、Xがの読み取り/書き込みプロパティであるFoo場合、コンパイラはに変換Foo.X += 5されFoo.SET_X_METHOD(Foo.GET_X_METHOD() + 5)ます(ただし、メソッドの名前は異なり、通常は名前でアクセスできません)。

autopropertyは、フィールドのように動作するようにプライベートフィールドにアクセスするget / setメソッドのペアを実装しますが、プロパティ外のコードの観点からは、autopropertyはget/のペアです。他のプロパティと同じようにメソッドを設定します。したがって、のようなステートメントFoo.X = 5;は。として変換されFoo.SET_X_METHOD(5)ます。C#コンパイラはそれをメソッド呼び出しと見なすだけであり、メソッドには読み取りまたは書き込みを行うフィールドを示すメタデータが含まれていないため、コンパイラは、のすべてのフィールドFooが書き込まれたことを認識していない限り、メソッド呼び出しを禁止します。

個人的には、構造型での自動プロパティの使用を避けることをお勧めします。クラスプロパティは更新通知などの機能をサポートできるため、自動プロパティはクラスで意味をなします。クラスの初期バージョンが更新通知をサポートしていない場合でも、それらのバージョンでフィールドではなく自動プロパティを使用することは、将来のバージョンでクラスのコンシューマーを作り直すことなく更新通知機能を追加できることを意味します。ただし、構造体は、フィールドのようなプロパティに追加したい機能のほとんどのタイプを有意義にサポートすることはできません。

さらに、フィールドとプロパティのパフォーマンスの違いは、クラスタイプよりも大きな構造の方がはるかに大きくなります。実際、大きな構造を避けるための推奨事項の多くは、この違いの結果です。大きな構造は、不必要にコピーしないようにすれば、実際には非常に効率的です。タイプの公開フィールドHexDecet<HexDecet<HexDecet<Integer>>>が含まれている巨大な構造体を持っていたとしても、のようなステートメントは、構造体標準(16Kを占める4096の整数)では巨大であるとしても、1つの整数を読み取り、それをに格納する必要があります。さらに、その要素を非常に簡単に更新できます。対照的に、..HexDecet<T>F0F15TFoo = MyThing.F3.F6.F9;MyThingFooMyThingMyThing.F3.F6.F9 += 26;F0F15自動プロパティの場合、ステートメントは、最終的にから4バイトのデータの読み取りに取り掛かる前に、1Kのデータをから一時的なFoo = MyThing.F3.F6.F9データにコピーする必要があります(それを呼び出し、次に64バイトのデータをからに) 。いや。さらに悪いことに、の値に26を追加しようとすると、のようなものが必要になります。MyThing.F3temp1temp1.F6temp2temp2.F9MyThing.F3.F6.F9var t1 = MyThing.F3; var t2 = t1.F6; t2.F9 += 26; t1.F6 = f2; MyThing.F3 = t1;

「可変構造タイプ」に関する長年の苦情の多くは、実際には読み取り/書き込みプロパティを持つ構造タイプに関する苦情です。プロパティをフィールドに置き換えるだけで、問題は解決します。

PS:プロパティが参照を保持するクラスオブジェクトにアクセスする構造を持つと便利な場合があります。たとえば、と言うことができるバージョンのArraySegment<T>クラスがVar foo[] = new int[100]; Var MyArrSeg = New ArraySegment<int>(foo, 25, 25); MyArrSeg[6] += 9;あり、最後のステートメントで次の要素(25 + 6)に9が追加されると便利です。foo。古いバージョンのC#では、それを行うことができました。残念ながら、フィールドがより適切であったフレームワークでの自動プロパティの頻繁な使用は、コンパイラに関する広範な苦情につながり、プロパティセッターが読み取り専用構造で無用に呼び出されることを可能にしました。その結果、プロパティセッターが実際に構造体のフィールドを変更するかどうかに関係なく、読み取り専用構造でプロパティセッターを呼び出すことは禁止されています。人々がプロパティセッターを介して構造体を可変にすることを単に控えていた場合(可変性が適切な場合にフィールドに直接アクセスできるようにする)、コンパイラーはその制限を実装する必要はありませんでした。

于 2012-10-01T15:46:11.003 に答える