静的チェックとランタイム チェックを有効にして、C# コード コントラクトを使い始めました。問題は、一部のコード コントラクト チェックがメソッドからメソッドへと重複する可能性があることです。これを回避する良い方法はありません。
私の希望は、静的アナライザーの警告をまったく回避し、可能であれば抑制しないことです。
例を考えてみましょう:
以下の簡単なクラスがあります。これは、ビジネス ロジック モデル クラスの通常の例です。
class Category
{
public string Name { get; set; }
}
class Article
{
public string Title { get; set; }
public string Content { get; set; }
public Category Category { get; set; }
}
一部のリフレクション ベースの手法 (MVC でのモデル バインディング、データベース マッピングなど) では、モデル プロパティのパブリック デフォルト コンストラクターとパブリック セッターを提供する必要があります。したがって、たとえばカテゴリの場合、Contract.Invariant(!string.IsNullOrEmpty(Name)) が常に true であることを保証することはできません。
次に、内部の CategoryRepository クラスに次のメソッドを作成します。すべての検証が以前に合格したと想定し、有効なカテゴリのみを受け入れます。
public void Add(Category category)
{
Contract.Requires(category != null);
Contract.Requires(!string.IsNullOrEmpty(category.Name));
...
}
ここまでは順調ですね。次に、同様のメソッドを ArticleRepository に追加します。
public void Add(Article article)
{
Contract.Requires(article != null);
Contract.Requires(!string.IsNullOrEmpty(article.Title));
Contract.Requires(!string.IsNullOrEmpty(article.Content));
Contract.Requires(article.Category != null);
Contract.Requires(!string.IsNullOrEmpty(article.Category.Name));
...
}
問題は次のとおりです。
1) 契約によって有効なカテゴリが期待されるすべての場所で、次のようなチェックを複製する必要があります。
Contract.Requires(category != null);
Contract.Requires(!string.IsNullOrEmpty(category.Name));
場合によっては、Contract.Assume メソッドでこれらのチェックを行う必要もあります。
2) 外部クラス (Article など) は、Category クラスのコントラクトをチェックする必要があります。LoWおよび基本的なカプセル化の原則に違反しているように見えます。
次の解決策を試しました:
1) 次のように、複製されたコードを Category クラスの純粋なメソッドに抽出します。
[Pure]
public static bool Valid(Category category)
{
if (category == null)
return false;
return !string.IsNullOrEmpty(category.Name);
}
使用契約は次のようになります。
Contract.Requires(Category.Valid(category));
あまり良い解決策ではなく、機能しません-静的アナライザーは満足していません。
2) カテゴリの不変条件を定義します。
[ContractInvariantMethod]
void Invariant()
{
Contract.Invariant(!string.IsNullOrEmpty(Name));
}
このソリューションは非常に優れており、Category クラスから不要なチェックを削除できますが、実際にはこの不変式は有効ではありません (たとえば、デフォルト コンストラクターでは)。そして、静的アナライザーはこの違反を正しく検出します。
コード コントラクトを静的アナライザーで使用するより便利な方法はありますか?