22

私はコードコントラクトにかなり精通していると言うかもしれません。私はユーザーマニュアルのほとんどを読んで理解し、かなり長い間それらを使用していますが、まだ質問があります。私がSOで「証明されていないコード契約」を検索すると、かなりの数のヒットがあり、それらの特定のステートメントが静的に証明できなかった理由をすべて尋ねます。私は同じことをして、私の特定のシナリオを投稿することができましたが(それはところで:

ここに画像の説明を入力してください)、

コードコントラクトの条件が証明できる、または証明できない理由を理解したいと思います。時々私はそれが証明できることに感銘を受けます、そして時々私は...まあ...それを丁寧に言うと:間違いなく感銘を受けません。これを理解したいのであれば、静的チェッカーが使用するメカニズムを知りたいです。経験から学ぶことは間違いありませんがContract.Assume、警告を消すためにいたるところにステートメントをスプレーしているので、それはコードコントラクトの目的ではないように感じます。グーグルは私を助けなかったので、皆さんにあなたの経験を聞いてみたいと思います:あなたはどんな(明白な)パターンを見ましたか?そして、何があなたに光を見させたのですか?

4

2 に答える 2

9

あなたの建設の契約は満たされていません。オブジェクトのフィールド(this.data)を参照しているため、他のスレッドがフィールドにアクセスし、その値をAssumeと最初のパラメーターの解像度および3番目のパラメーターの解像度の間で変更する場合があります。(ei、それらは3つの完全に異なるアレイである可能性があります。)

配列をローカル変数に割り当ててから、メソッド全体でその変数を使用する必要があります。次に、他のスレッドが参照を変更する機能を持たないため、アナライザーは制約が満たされていることを認識します。

var localData = this.data;
if (localData == null) return;
byte[] newData = new byte[localData.Length]; // Or whatever the datatype is.
Array.Copy(localData, newData, localData.Length); // Now, this cannot fail.

これには、制約を満たすだけでなく、実際には、多くの場合、コードをより堅牢にするという追加の利点があります。

これがあなたの質問への答えにつながることを願っています。静的チェッカーを含むバージョンのVisualStudioにアクセスできないため、実際に直接質問に答えることはできませんでした。(私はVS2008 Proを使用しています。)私の答えは、コードの私自身の目視検査が結論するものに基づいており、静的コントラクトチェッカーは同様の手法を使用しているようです。私は困惑しています!そのうちの1つを入手する必要があります。:-D

更新:(従うべき多くの憶測)

振り返ってみると、(静的チェッカーにアクセスしなくても)証明できるものとできないものをかなり正確に推測できると思います。他の回答で述べられているように、静的チェッカーは手続き間分析を行いません。したがって、(OPのように)マルチスレッド変数アクセスの可能性が迫っているため、静的チェッカーはローカル変数(以下に定義)のみを効果的に処理できます。

「ローカル変数」とは、他のスレッドからアクセスできない変数を意味します。これには、メソッドで宣言された変数、またはパラメーターとして渡された変数が含まれます。ただし、パラメーターがreforで装飾されてoutいるか、変数が匿名メソッドでキャプチャされている場合を除きます。

ローカル変数が値型の場合、そのフィールドもローカル変数です(再帰的になど)。

ローカル変数が参照型の場合、フィールドではなく、参照自体のみをローカル変数と見なすことができます。これは、メソッド内で構築されたオブジェクトにも当てはまります。コンストラクター自体が、構築されたオブジェクトへの参照をリークする可能性があるためです(たとえば、キャッシュ用の静的コレクションなど)。

静的チェッカーが手続き間分析を行わない限り、上記で定義されたローカルではない変数について行われた仮定はいつでも無効になる可能性があるため、静的分析では無視されます。

例外1:文字列と配列はランタイムによって不変であることがわかっているため、文字列または配列変数自体がローカルである限り、それらのプロパティ(長さなど)は分析の対象になります。これには、他のスレッドによって変更可能な配列の内容は含まれません。

例外2:配列コンストラクターは、構築された配列への参照をリークしないことがランタイムによって認識されている場合があります。したがって、メソッド本体内で構築され、メソッドの外部にリークされない(別のメソッドにパラメーターとして渡される、非ローカル変数に割り当てられるなど)配列には、ローカル変数と見なされる可能性のある要素が含まれます。

これらの制限はかなり厄介なようで、これを改善できるいくつかの方法を想像できますが、何が行われたのかわかりません。理論的には、静的チェッカーで実行できる他のいくつかのことがあります。それを手元に置いている人は、何が行われ、何が行われていないかを確認する必要があります。

  • コンストラクターがオブジェクトまたはそのフィールドへの参照をリークしていないかどうかを判断し、そのように構築されたオブジェクトのフィールドをローカル変数と見なすことができます。
  • メソッドに渡された参照型が、そのメソッドの呼び出し後もローカルと見なすことができるかどうかを判断するために、他のメソッドでリークなしの分析を行うことができます。
  • ThreadStaticまたはThreadLocalで装飾された変数は、ローカル変数と見なすことができます。
  • リフレクションを使用して値を変更する可能性を無視するオプションを指定できます。これにより、参照型のプライベート読み取り専用フィールドまたは静的プライベート読み取り専用フィールドを不変と見なすことができます。また、このオプションを有効にすると、構造内でのみアクセスされ、リークされないプライベート変数または内部変数Xをローカル変数lock(X){ /**/ }と見なすことができます。ただし、これらのことは事実上、静的チェッカーの信頼性を低下させるので、それはちょっと不確かです。

多くの新しい分析を開く可能性のある別の可能性は、変数とそれらを使用するメソッド(および再帰的になど)を特定の一意のスレッドに宣言的に割り当てることです。これは言語への主要な追加ですが、それだけの価値があるかもしれません。

于 2011-02-17T17:13:03.587 に答える
5

簡単な答えは、静的コードアナライザーは非常に制限されているように見えるということです。たとえば、それは検出しません

readonly string name = "I'm never null";

不変であるとして。私がMSDNフォーラムで収集できることから、すべてのメソッドをそれ自体で分析します(パフォーマンス上の理由から、はるかに遅くなる可能性があると考えるべきではありません)。これにより、コードを検証する際の知識が制限されます。

正しさを証明するという学術的に高い目標と仕事を成し遂げることができることの間のバランスをとるために、私は個々のメソッド(または必要に応じてクラスさえ)をで飾ることに頼りました

[ContractVerification(false)]

ロジックに多くの仮定を振りかけるのではなく。これはCCを使用するためのベストプラクティスではないかもしれませんが、静的チェッカーオプションのチェックを外さずに警告を取り除く方法を提供します。このようなメソッドの事前/事後条件チェックを失わないようにするために、通常、目的の条件でスタブを追加してから、除外されたメソッドを呼び出して実際の作業を実行します。

私自身のコードコントラクトの評価では、公式のフレームワークライブラリのみを使用していて、レガシーコードがあまりない場合(たとえば、新しいプロジェクトを開始する場合)は素晴らしいと思います。それ以外は、喜びと苦痛が入り混じったバッグです。

于 2011-02-17T17:01:27.200 に答える