web-dev-qa-db-ja.com

自動プロパティと構造が混ざらない?

この投稿 と答えている間、いくつかの小さな構造をあちこち見て、予期せず次のことに遭遇しました。

次の構造体は、intフィールドを使用しても完全に合法です。

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Legal assignment.
    } 

    public int Size; 
}

ただし、自動プロパティを使用する次の構造はコンパイルされません。

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Compile-Time Error!
    } 

    public int Size{get; set;}
}

返されるエラーは、「すべてのフィールドが割り当てられるまで、「this」オブジェクトは使用できません」です。これは構造体の標準的な手順であることを知っています。プロパティのバッキングフィールドは、構造体のコンストラクター内から直接(プロパティのセットアクセサーを介さずに)割り当てる必要があります。

解決策は、明示的なバッキングフィールドを使用することです。

struct MyStruct
{ 
    public MyStruct(int size)
    {
        _size = size;
    }

    private int _size;

    public int Size
    {
        get { return _size; }
        set { _size = value; }
    }
}

(VB.NETではすべてのフィールドが最初に作成されたときに自動的に0/null/falseに初期化されるため、VB.NETにはこの問題はありません。)

C#の構造体で自動プロパティを使用する場合、これは残念な制限のようです。概念的に考えると、少なくとも自動プロパティの場合、構造体のコンストラクター内でプロパティセットアクセサーを呼び出すことを許可する例外があるため、これが適切な場所ではないのかと思いました。

これは小さな問題ですが、ほとんどEdgeケースですが、他の人がこれについてどう思っているのかと思っていました...

64
Mike Rosenblum

C#6以降:これは問題ではなくなりました


Becore C#6では、これを機能させるためにデフォルトのコンストラクターを呼び出す必要があります。

public MyStruct(int size) : this()
{
    Size = size;
}

ここでの大きな問題は、変更可能な構造体があることです。これはneverが良い考えです。私はそれを作るでしょう:

public int Size { get; private set; }

技術的には不変ではありませんが、十分に近いです。

C#の最近のバージョンでは、これを改善できます。

public int Size { get; }

これは、コンストラクタでonlyを割り当てることができるようになりました。

83
Marc Gravell

これを修正するには、最初にデフォルトのコンストラクターを呼び出します。

struct MyStruct 
{
    public MyStruct(int size) : this() 
    {
        this.Size = size; // <-- now works
    }

     public int Size { get; set; }
}
10
Stormenet

この問題のもう1つのあいまいな回避策は、 Managed Extensibility Framework の一時的なTupleクラスで発見されたものです( KrzysztofKoźmic を使用):

public struct TempTuple<TFirst, TSecond>
{
    public TempTuple(TFirst first, TSecond second)
    {
        this = new TempTuple<TFirst, TSecond>(); // Kung fu!
        this.First = first;
        this.Second = second;
    }

    public TFirst First { get; private set; }
    public TSecond Second { get; private set; }

(Codeplexの完全なソースコード: Tuple.cs

CS0188 のドキュメントが更新されて追加されていることにも注意します。

構造体コンストラクターでプロパティを初期化しようとしたときにこのエラーが発生した場合の解決策は、コンストラクターパラメーターを変更して、プロパティ自体ではなくバッキングフィールドを指定することです。 自動実装されたプロパティにはバッキングフィールドがないため、コンストラクターから初期化できないため、構造体では避けてください。

したがって、このガイダンスは、この問題が発生したときに構造体で古いスタイルのプロパティを使用することであることを意味します。

8
Daniel Fortunov