6

コンストラクターによって強制される、特定の契約に関して常に有効になるように構造体を強制したいと思います。しかし、契約はdefaultオペレーターによって違反されています。

たとえば、次のことを考慮してください。

struct NonNullInteger
{
    private readonly int _value;

    public int Value
    {
        get { return _value; }
    }

    public NonNullInteger(int value)
    {
        if (value == 0)
        {
            throw new ArgumentOutOfRangeException("value");
        }

        _value = value;
    }
}

// Somewhere else:
var i = new NonNullInteger(0); // Will throw, contract respected
var j = default(NonNullInteger); // Will not throw, contract broken

回避策として、構造体をクラスに変更して、新しいインスタンスを初期化するときにコンストラクターが常に呼び出されるようにしました。しかし、構造体で同じ動作を得る方法は絶対にないのだろうか?

4

4 に答える 4

6

クラスとは異なり、構造体には常にデフォルトのパラメーターなしのコンストラクターがあるため、これを行う方法がわかりません。構造体の書き方によっては、値 0 を防ぐことはできません。

構造体には、明示的なパラメーターなしのコンストラクターを含めることはできません。構造体メンバーは、デフォルト値に自動的に初期化されます。

于 2011-11-27T18:15:35.873 に答える
3

1つのアプローチは、デフォルト値が契約を満たすように物事を調整することです。

struct NonNullInteger
{
    private readonly int _valueMinusOne;

    public int Value
    {
        get { return _valueMinusOne + 1; }
    }

    public NonNullInteger(int value)
    {
        if (value == 0)
        {
            throw new ArgumentOutOfRangeException("value");
        }

        _valueMinusOne = value - 1;
    }
}
于 2011-11-27T21:37:52.447 に答える
1

デフォルトの状態は無効であるため、この場合は不変クラスが望ましいです。メモリ使用量に関しては少しコストがかかりますが、これらを非常に多く使用しない限り、問題にはなりません。実際には、「ゼロ以外の数」の制約は、クラスに入れるのではなく、各メソッドの契約レベルでおそらくより適切に処理されます。

本当にコントラクトを強制したい場合は、例外をコンストラクターではなく Value ゲッターに入れます。次に、0 の値が含まれていた場合に例外をスローするという契約があります。ここでの唯一の利点は、何も言わずにゼロ値を使用しないことです。欠点は、値を使用するたびに比較できるようになったことです。

于 2011-11-27T18:22:18.553 に答える
1

あなたが望むものを正確に達成することはできませんが、ゲッターで検証することができます:

public int Value
{
    get 
    { 
        if (_value == 0) 
            throw new InvalidOperationException("Use of uninitialized struct"); 
        return _value; 
    }
}
于 2011-11-27T18:34:20.413 に答える