web-dev-qa-db-ja.com

C#でCodeContractsを本当に好きにしようとしています

ついに、.NET 3.5/4.0 Frameworksに追加されたすべての新しい機能でキャッチアップをプレイしています。ここ数日、私はCodeContractsを使用しており、本当にそれらを好きになるように努力しています。他の人々がC#でのCodeContractsの実装についてどう思うか知りたいです。具体的には、インターフェイスのコントラクトクラス、コントラクトインバリアントのコントラクトメソッドなどをどのように整理していますか?

コントラクトが提供する検証が好きです。一見するとコントラクトは見栄えがします。数行の簡単な行で、コードを実行する前に、Ni​​ceビルドチェックを取得できます。残念ながら、コードコントラクトがC#で実装される方法を理解するのに苦労しています。コントラクトをドキュメント化するよりも、コードが乱雑になっています。コントラクトを最大限に活用するために、仮定やアサートなどでコードを散らかしています(これは良いことだと言う人もいると思います)。しかし、以下の私の例のいくつかが示すように、それは1行の単純な行を4行または5行に変え、代替アプローチ(つまり、アサート、例外など)に対して私の意見に十分な価値を追加しません。

現在、私の最大の不満は次のとおりです。

インターフェース契約:

[ContractClass(typeof(IInterfaceContract))]
  public interface IInterface
  {
    Object Method(Object arg);
  }

  [ContractClassFor(typeof(IInterface))]
  internal abstract class IInterfaceContract
  {
    private IInterfaceContract() { }

    Object IInterface.Method(Object arg)
    {
      Contract.Requires(arg != null);
      Contract.Ensures(Contract.Result<Object>() != null);
      return default(Object);
    }
  }

これは私にとっては非常に不便なことのように感じられます。属性または何らかの組み込み言語サポートの形式のいずれかを介して、要件を文書化するためのより明確な方法があったといいのですが。インターフェイスを実装する抽象クラスを実装しなければならないという事実は、コントラクトを指定できるようにするだけでも、面倒なように思えます。

コード膨張:

typeof(Action<>).MakeGenericType(typeof(Object);

すぐに入手できる情報を検証するためだけにいくつかの前提が必要です。アナライザーが知っているのは、Typeで動作しているため、限られた知識で動作する必要があることだけですが、それでも、1行のコードで次のように書き直す必要があることに苛立ちを感じています。

var genericAction = typeof(Action<>);

Contract.Assume(genericAction.IsGenericType);
Contract.Assume(genericAction.GetGenericArguments().Length == 1);

genericAction.MakeGenericType(typeof(Object));

文書化された状態を維持するために(はい、私は、ContractVerificationAttributeを使用してメソッド/クラスなどに対してこれをオフにできるか、またはSuppressMessageAttribbuteを使用して特定のメッセージをターゲットにできることを知っていますが、コードがすぐに抑制などで散らかされるので、目的を無効にするようです。

また、

  public class MyClass
    : IInterface
  {
    private readonly Object _obj;

    public Object Property
    {
      get
      {
        Contract.Ensures(Contract.Result<Object>() != null);
        return _obj;
      }
    }

    public MyClass(Object obj)
    {
      Contract.Requires(obj != null);

      _obj = obj;
    }
  }

objはnullでない必要があり、変更できない読み取り専用フィールドに設定されていますが、nullを返さないという私のプロパティ要件を証明できるように、クラスに「マークアップ」メソッドを追加する必要があります。

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

他にもたくさんありますが、おそらく十分に正当化していると思います。コードコントラクトを "好き"にして、コードの煩雑さを解消するために、私よりはるかに賢い人々の洞察に感謝します。コードをより適切に構成する方法、回避策の不安定さの問題などについての洞察は、高く評価されます。

ありがとう!

59
Chris Baxter

これは私にとっては非常に不便なことのように感じられます。属性または何らかの組み込み言語サポートの形式のいずれかを介して、要件を文書化するためのより明確な方法があったといいのですが。

CCチームは、属性にラムダなどを含めることはできないため、属性を使用するだけでは十分に強力ではないと述べています。彼らはcould[NotNull]のようなものを含めますが、 そうしないように選択 を持っていますできるだけ一般的です。

CCが(拡張C#の一部ではなく)ライブラリである理由の1つは、CCがall.NET言語全体でサポートされていることです。

チームの推論についての詳細は こちら を参照してください。

これを実際に使用するという点では、これまでのところ、インターフェイスコントラクトをインターフェイスと同じファイルに保存しているだけです。つまり、すべて同じ場所に文書化されています。それは改善する必要があるものです:)

コードブロート[...]

2番目の苦情はおそらく実装可能なものです (コード契約フォーラム )に投稿することをお勧めします。 (編集:誰からしい 既に持っている だが、まだ答えはない。)

ただし、十分に指定されていない契約では、契約を取り巻く前提条件がさらに必要になる場合が常にあります。 .NETフレームワークでこのようなケースに遭遇した場合、 Missing Contracts on Librariesスレッド でコントラクトを追加するように要求できます。

さらに、[...]

これは 対処済み です。自動プロパティがある場合は、null以外の不変条件を追加するだけで、事前/事後条件が生成されます。

public class MyClass : IInterface
{
    private Object Property { get; set; }

    [ContractInvariantMethod]
    private void Invariants()
    {
        Contract.Invariant(Property != null);
    }
}

いずれにしても、クラスには他の不変式が含まれることになるので、それほど大きな問題ではありません。

26
porges

そうです、コントラクトは混乱を招きますが、 [〜#〜] pex [〜#〜] がコードコントラクトを読み取り、25の単体テストと100%のコードカバレッジを生成すると、価値があると思います。 PEXをまだ使用していない場合は、コードコントラクトの最大のメリットを逃しています。 コントラクト付きPEX を使用するためのスターターガイドを次に示します。

12
Matt Dotson

私も本当にそれが好きで、新しいプロジェクトでの実装にかなり遠くまで行きました。小さな小さな問題の重みでそれをあきらめるまでは。

MicrosoftがSpec#を復活させたいです。コードコントラクトが言語間で利用できるライブラリとして記述されていることは知っていますが、静的分析の速度でさえコンパイラのサポートを求めています。

この男は同じ点をいくつか挙げています: http://earthli.com/news/view_article.php?id=218

とにかく、契約違反の問題は1つもありませんでしたが、イライラの蓄積がありました。

  • 静的解析は非常に遅いです。バックグラウンドで実行するように設定できますが、無視するのは非常に簡単です。
  • たとえば、静的分析は優れていません。
    • 単純な制御フローのみを処理します
    • 読み取り専用フィールドが不変であるとは想定していません
    • コレクションは静的チェッカーでは処理されません(ランタイムのみ)
  • 委任契約なし
  • 何らかの方法でリシャーパー警告を積極的に抑制しない限り、リシャーパーではうまく機能しません
    • Resharperは楽観的です。 Contract.Assume(thing != null)のように、それがそうであるかもしれないというヒントを与えるまで、メソッドパラメータはnullではないと仮定します。
  • 多くの警告と提案を生成する可能性があり、あいまいなソースから生じる場合があります。
    • チームメンバーが注意をそらす瞬間、警告の数は誰にとっても圧倒的になります。
  • インターフェイスの抽象コントラクトクラスを指定する必要があるのは苦痛です。
  • ランタイムコントラクトはILリライティングを使用します。これは、PostSharpなどの他のリライティングソリューションではうまく機能しないようです(。
    • ILの書き換えの結果、編集継続は使用できません
  • 契約はリスコフ置換の原則に非常に強く固執します。サブタイプは事前条件を緩和することはできません。原則的にはいいのですが、契約し過ぎたサードパーティのコードで作業するのは面倒だと思います。
9
Andy

コードの膨張が心配な場合は、代わりに アスペクト指向プログラミング を検討することをお勧めします。

これにより、アスペクトでコントラクトを指定し、クラスまたはクラスのメソッドをコントラクトでラップできます。コントラクトが複数のクラスで使用される場合、これはこれを実装するためのよりクリーンな方法になります。

残念ながら、C#でのAOPのサポートは素晴らしいものではありませんが、この機能を追加するライブラリがいくつかあります。

また、コントラクトで見つけた他のC#実装に対しても同じように感じているので、フードを持ち上げて本格的な使用を開始するまで、最初はすべて問題がないように見えます。

3
eaglestorm

インターフェイスと抽象クラスのコードコントラクトを作成する時間を節約できるVisual Studio 2010のプラグインを作成しました。 http://visualstudiogallery.msdn.Microsoft.com/en-us/d491911d-97f3-4cf6- 87b0-6a2882120acf

3
Jarek Kardas

実際、インターフェースの実装は素晴らしいと思います。インターフェースや通常のコードが煩雑になることはなく、ソリューション内のいくつかのフォルダー、またはインターフェースと同じクラスファイルに、どちらでも好きなようにきちんと保存できます。

なぜ必須でオブジェクトを不変に追加するのですか?必要な量だけ追加する必要があります。

コードの膨張についてのあなたのポイントを理解しましたが、それはコードコントラクトとのトレードオフだと思います。追加のチェック/セキュリティは、追加のコード、少なくとも現在の実装方法を意味します。私はインターフェイスで可能な限り多くのコントラクトを維持しようとします。それは、少なくともコードの肥大化の痛みを和らげるのに役立つはずです。

2
koenmetsu