7

コード コントラクトとその使用法に関するいくつかの質問があります。いくつかのプロパティを持つクラスがあるとしましょう (以下の例を参照):

class Class1
{
    // Fields
    private string _property1;       //Required for usage
    private List<object> _property2; //Not required for usage

    // Properties
    public string Property1 
    { 
        get
        {
            return this._property1;
        }            
        set
        {
            Contract.Requires(value != null);
            this._property1 = value;
        } 
    }
    public List<object> Property2 
    { 
        get
        {
            return this._property2;
        }
        set
        {
            Contract.Requires(value != null);
            this._property2 = value;
        }
    }

    public Class1(string property1, List<object> property2)
    {
        Contract.Requires(property1 != null);
        Contract.Requires(property2 != null);

        this.Property1 = property1;
        this.Property2 = property2;
    }

    public Class1(string property1)
        : this(property1, new List<object>())
    { }
}

私が達成したいことについてのいくつかの説明:

(a) property1 は必須フィールドです。property2 は、オブジェクトの通常の使用には明示的に必要ありません。

次の質問があります。

  1. property2 のコントラクトを気にする必要がありますか。property2 は必須フィールドではないため、コントラクトが必要です。property2 にコントラクトを置くことは、それが実際にオブジェクトの通常の使用に必要であることを示していますか?

  2. property2 は明示的に必須ではありませんが、それが null になる理由は考えられないため、setter で定義されたコントラクトです。property2 でコントラクトを定義すると、コードを呼び出す際の null チェックが減るのではないでしょうか? これにより、バグが減り、コードの保守性が向上するはずです。この仮定は正しいですか?

  3. それが正しい場合、property2 が決して null にならないようにコードを呼び出すにはどうすればよいですか? Contract.Invariant(property2 != null); を使用しますか? コンストラクターの Contract.Ensures(property2 != null) 、または Init() の Contract.Ensures(property2 != null) 、またはセッターの Contract.Ensures(property != null) ? (つまり、Contract.Ensures(property2 != null) を使用する場合、どこに配置されますか)?

質問が単純に見えたら申し訳ありません。この問題についての考えと、皆さんがベスト プラクティスと考えていることを探しています。

4

4 に答える 4

3

これは、契約に関して私が推奨するものです。

    class Class1
    {
        // Fields
        private string _property1;       //Required for usage
        private List<object> _property2; //Not required for usage

        // Properties
        public string Property1
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() != null);
                return this._property1;
            }
            set
            {
                Contract.Requires(value != null);
                this._property1 = value;
            }
        }

        public List<object> Property2
        {
            get
            {
                Contract.Ensures(Contract.Result<List<object>>() != null);
                return this._property2;
            }
            set
            {
                Contract.Requires(value != null);
                this._property2 = value;
            }
        }

        public Class1(string property1, List<object> property2)
        {
            Contract.Requires(property1 != null);
            Contract.Requires(property2 != null);

            this.Property1 = property1;
            this.Property2 = property2;
        }

        public Class1(string property1)
            : this(property1, new List<object>())
        {
            Contract.Requires(property1 != null);
        }

        [ContractInvariantMethod]
        private void ContractInvariants()
        {
            Contract.Invariant(_property1 != null);
            Contract.Invariant(_property2 != null);
        }
    }

プロパティにはパブリックな動作契約があり、フィールド値を変更してパブリック契約に違反する可能性のあるロジックを Class1 に追加するときに、後で発生する可能性のあるエラーをインバリアントがキャッチします。または、フィールドを読み取り専用にすることができる場合 (およびセッターを削除する場合)、不変条件は必要ありません。

于 2011-12-05T17:24:12.090 に答える
2

ここには個人的な好みがたくさんあると思いますが、私の2セント...

1)オプションの引数としてproperty2を持つようにコンストラクターを調整したいと思うでしょうし、おそらく誘惑されるでしょう:

Class1(string property1, List<object> property2 = new List<object>())      
{      
    Contract.Requires(property1 != null);      
    Contract.Requires(property2 != null);      

    this.Property1 = property1;      
    this.Property2 = property2;      
} 

2)3を参照

3)コードを呼び出す際のヌルチェックを減らす前に、個人的には

Contract.Ensures(property2 != null) 

ゲッターで-VS11CTPが定義のツールチップにコントラクトを表示するのを見たので、これを見ることができれば、nullをチェックする必要がないことがわかります。私はRequiresセッターを維持します。

于 2011-12-05T16:22:35.090 に答える
1

多くの場合、オブジェクトにリストがある場合、オブジェクトに作成を任せます。したがって、消費者がリストに追加したい場合は、最初にそれを取得する必要があります。このクラスの使用法によっては、これがより良いルートである可能性があります。

class Class1
{
    // Fields
    private string _property1;       //Required for usage
    private List<object> _property2 = new List<object>(); //Not required for usage

    // Properties
    public string Property1 
    { 
        get
        {
            return this._property1;
        }            
        set
        {
            Contract.Requires(value != null);
            this._property1 = value;
        } 
    }
    public List<object> Property2 
    { 
        get
        {
            return this._property2;
        }
        //We don't have a setter.
    }

    public Class1(string property1)
    {
        Contract.Requires(property1 != null);

        this.Property1 = property1;
    }
}
于 2011-12-05T16:31:05.767 に答える
1

1/2: プロパティ 2 にコントラクトを持つことは、それが強制したい動作である場合に適切です。つまり、null であってはならず、null チェックの必要性と null に関する潜在的なバグが確実に減少します。

3: この質問に答えるために、私はあなたのクラスを次のように書き直しました。

class Class1
{
    //Properties
    public string Property1 { get; set; }
    public List<object> Property2 { get; set; }

    public Class1(string property1, List<object> property2)
    {
        Contract.Requires(property1 != null);
        Contract.Requires(property2 != null);

        Property1 = property1;
        Property2 = property2;
    }

    public Class1(string property1)
        : this(property1, new List<object>())
    { 
        Contract.Requires(property1 != null);
    }

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(Property1 != null);
        Contract.Invariant(Property2 != null);
    }
}

クラスに自動実装されたプロパティがある場合は、 を使用できますContract.Invariant()。これにより、プロパティ内の明示的なコントラクトが冗長になるため、次のコードは必要ありません。

public string Property1 
{ 
    get
    {
        Contract.Ensures(Contract.Result<string>() != null);
        return this._property1;
    }            
    set
    {
        Contract.Requires(value != null);
        this._property1 = value;
    } 
}

それは財産保護の世話をします。プロパティが決して null にならないようにするには、コンストラクターに Contract.Requires(property1 != null) を追加します。

この回答が 3 年遅れていることは承知していますが、役に立つかもしれません。

ソース: http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf

于 2015-03-13T17:04:00.440 に答える