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