59

私はついに、.NET 3.5/4.0フレームワークに追加されたすべての新しいものに追いつきました。ここ数日、私はCodeContractsを使用しており、それらを気に入ってもらうために一生懸命努力しています。他の人がC#でのCodeContractsの実装についてどう思うか興味がありますか?具体的には、インターフェイスのコントラクトクラス、コントラクトインバリアントのコントラクトメソッドなどをどのように整理していますか?

私は契約が提供する検証が好きです、一見すると彼らは見栄えがします。数行の簡単な行で、コードを実行する前に、いくつかの優れたビルドチェックを取得できます。残念ながら、コードコントラクトがC#で実装されているという感覚を乗り越えるのに苦労しています。彼らは、コントラクトを文書化するよりもコードを乱雑にしているのです。そして、コントラクトを最大限に活用するために、私は自分のコードに仮定や主張などを散らかしています(これは良いことだと言う人もいます)。しかし、以下の私の例のいくつかが示すように、それは単一の単純な行を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);
}

まだまだありますが、私はおそらく十分に怒鳴っていると思いました。コードコントラクトを「好き」にして、このコードの乱雑さを解消するために、私よりもはるかに賢い人々の洞察を本当に感謝しています。コードをより適切に構造化する方法、不安定さの問題を回避する方法などについての洞察をいただければ幸いです。

ありがとう!

4

6 に答える 6

26

これは私にはそのような手がかりのように感じます。属性または何らかの形式の組み込み言語サポートを介して、要件を文書化するためのよりクリーンな方法があればいいのにと思います。

CCチームは、属性にラムダのようなものを含めることができないため、属性の使用は十分に強力ではないと述べています。CCを可能な限り一般的にしようとしているため、次のようなものを含めることができますが、そうしないこと[NotNull]を選択しました。

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

チームの推論について詳しくは、こちらをご覧ください。

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

コード膨張[...]

2番目の苦情は、おそらく実装できるものです。コードコントラクトフォーラムに投稿することをお勧めします。(編集:誰かがすでに持っているようですが、まだ答えはありません。)

ただし、特定されていない契約には、それらを取り巻くより多くの仮定が必要になる場合が常にあります。.NET Frameworkでこのようなケースに遭遇した場合は、ライブラリスレッドの不足しているコントラクトにコントラクトを追加するように要求できます。

また、[...]のようなケースを取る

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

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

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

とにかく、クラスの他の不変条件になってしまう可能性があるので、それほど大したことではありません。

于 2010-06-23T04:41:34.050 に答える
12

ええ、コントラクトは雑然としていますが、 PEXがコードコントラクトを読み取り、25の単体テストと100%のコードカバレッジを生成するのは価値があると思います。PEXをまだ使用していない場合は、コードコントラクトの最大のメリットを逃しています。これは、コントラクトでPEXを使用するためのスターターガイドです。

于 2010-06-21T04:30:25.810 に答える
9

私もそれが本当に好きで、新しいプロジェクトでの実装をかなり進めましたが、それで抱えていた小さな問題の重みで諦めました。

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

この男は同じ点のいくつかを提起します:http ://earthli.com/news/view_article.php?id = 2183

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

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

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

于 2010-07-15T15:49:39.087 に答える
3

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

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

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

また、Contractsで見つけたのと同じように、他のC#実装に対しても同じ気持ちを持っているので、フードを持ち上げて本格的に使い始めるまでは、最初はすべて問題ないようです。

于 2010-06-21T00:26:45.540 に答える
2

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

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

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

于 2010-06-21T13:59:34.783 に答える