Weitere ähnliche Inhalte
Ähnlich wie 基礎からのCode Contracts (20)
Mehr von Yoshifumi Kawai (19)
基礎からのCode Contracts
- 2. Profile
Twitter
=> @neuecc
Blog => http://neue.cc/
HNは"neuecc" 読むときは“のいえ”で
ドメン繋いだだけで特に意味はなく発音不能のた
め(ccは声に出しにくいのでスルーという適当対応)
Microsoft
MVP for Visual C#(2011/4-)
公開してるラブラリとか
linq.js
DynamicJson
Chaining Assertion
DbExecutor <- (ちょっとだけ)Code Contracts使った
- 5. 何か動かないよ?
よくあるnullチェックをしてみようと思った
Contract.Requiresは事前条件
引数がnullだったら契約違反という感じにしたい
static void Hoge(string arg)
{
Contract.Requires(arg != null);
}
が、実行しても無反応
Conditional属性がついているのでコンパル時に
消える(条件付きメソッド、DEBUGとかでお馴染み)
条件はCONTRACTS_FULL(但し自分で足す意味はない)
- 6. 何か動かないよ? Part2
よくあるnullチェックをしてみようと思った again
Contract.Requires<TException>も事前条件
引数がnullだったら契約違反で例外ぶん投げたい
static void Hoge(string arg)
{
Contract.Requires<ArgumentNullException>(arg != null);
}
が、変なゕサートが飛ぶ
そしてゕプリは強制終了
リラターがmustだと?
- 7. つまるところ
Code Contractsの利用にはリラターが必要
最終的な配布物はコンパラオプションで契約用コードを
取り除く。従って実行効率にも影響しない。
http://ja.wikipedia.org/wiki/契約プログラミング
契約は取り除かれなければならない
そのためにはラブラリだけでは不可能で、コン
パル時にバナリを弄る必要がある
契約の実現のため、現状はバナリ改変している
真に標準搭載されたと言えるのはリラターがコン
パラと統合された時かもね
- 8. Code Contractsの構成物
必須
Contractクラスなどコードに記述するマーカー
.NET 4で現状標準搭載されているのはこれだけ
バナリリラター(ccrewrite.exe)
オプション
参照ラブラリ生成(ccrefgen.exe)
ドキュメント生成(ccdocgen.exe)
静的チェッカー(cccheck.exe)
cccheckはPremium Editionのみ
静的チェックなしの場合は、例外orゕサートを投げる実
行時チェックという形になる
- 10. Code Contractsのンストール
DevLabs: Code Contracts
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
Standard Edition (Visual Studio Professional)
ccrewrite, ccrefgen, ccdocgen
Premium Edition (Visual Studio Premium,Ultimate)
Standard + cccheck
Visual
Studio Express Editionでは使えない
静的チェッカーの有無も大きなところ
契約の正しさが実行時じゃないと確認出来ないとい
うのは、何が正しいのか分からない初学者にとって
学習が困難になる
- 11. プロジェクトのプロパテゖ
Contractsタブが追加されてる
に、Code
チェックボックスをオンにすると各機能が有効に
パラメータがいっぱいあって困る?
マニュゕルを見れば勿論、説明がある
日本語で?zeclさんのスラドを見よう!
http://d.hatena.ne.jp/zecl/20110213/p2
- 12. 事前条件
Contract.Requires
無印と<TException>とEndContractBlockの三種
無印はコンパラ生成のContractExceptionを投げる
コンパラ生成なので型判別したcatchは不可能
<TE>の場合は指定した例外を投げる
EndContractBlockはif-then-throwを<TE>に変換する
// これと
if (arg == null) throw new ArgumentNullException("arg");
Contract.EndContractBlock();
// これは大体等しい
Contract.Requires<ArgumentNullException>(arg != null);
- 13. 事前条件の違い
EndContractBlockはレガシー環境用
バナリリラターがある環境が前提なら不要
Assembly Modeの選択
Requires, Requires<TE>はStandard Contract
EndContractBlockを使う場合はCustom Parameter
無印と<TE>ではリラト時に残るレベルが違う
無印の場合はReleaseRequiresでは除去される
DebugはFull、ReleaseではPreまたはReleaseを推奨
- 14. 事後・不変・ンターフェス
事後 : Contract.Ensures
戻り値を表すContract.Result<T>とセットで使うこと
が多い
不変 : Contract.Invariant
ContractInvariantMethod属性とセットで
cimコードスニペットを使えば展開される
ンターフェスへの契約
書くのがヘンテコで面倒くさい
cintfコードスニペットを使えば展開される
- 16. 動かしたけど嬉しさ少なめ?
静的チェッカなしだと、どうも地味
Premiumの人なら関係ないですねShit!
そんな物足りなさを感じるゕナタにVisualな贈り物
VS拡張:Code Contracts Editor Extensions
http://visualstudiogallery.msdn.microsoft.com/85f0aa38
-a8a8-4811-8b86-e7f0b8d8c71b
契約がIntelliSenseに表示される!
FreeなのでVS Professionalの人でもOK
- 19. 但し制限も色々あり
コンストラクタは表示されません
ジェネリックメソッドは表示されません
Enumerable.Rangeは表示されるのにRepeatは表示さ
れなかったりしてるのが確認できます
つまるところLINQのメソッドは全滅
yieldが含まれると表示されません
dynamicが含まれると表示されません
よく落ちます(落ちたらVS再起動まで復活しない)
Editor Extensionsに関してはアルファ版だと思って
暖かく見守りましょう
- 21. 嬉しいこと1
引数名を文字列で指定しなくてもいい
リラターが埋め込んでくれるから
コードスニペットcrenは文字列指定付きだけど、個
人的にはそれは不要だと思う
// この文字列で引数名を書くのがかなりヤだった
if (arg == null) throw new ArgumentNullException("arg");
// それをCode Contractsではこう書き、そしてこれは
Contract.Requires<ArgumentNullException>(arg != null);
// バナリリラト後にこうなる
// 最後の"arg != null"がメッセージで、
// 条件を文字列として生成してくれているのが分かる
__ContractsRuntime.Requires<ArgumentNullException>(
arg != null, null, "arg != null");
- 22. 嬉しいこと2
ンターフェスに契約すると、それを実装する
ものへは何も書かなくても自動で埋め込まれる
// こうしてンターフェスへの契約を作ると(cintfスニペット推奨)
[ContractClass(typeof(IHogeContract))]
public partial interface IHoge
{
void Show(string arg);
}
[ContractClassFor(typeof(IHoge))]
abstract class IHogeContract : IHoge
{
public void Show(string arg)
{
Contract.Requires<ArgumentNullException>(arg != null);
}
}
- 23. それはとっても嬉しいなって
class ClassA : IHoge
{
// 何も書いていませんが
// Contract.Requires<ArgumentNullException>(arg != null)が埋めこまれる
public void Show(string arg)
{
Console.WriteLine(arg);
}
}
class ClassB : IHoge
{
// 全てのメソッドにif(arg == null) throwを書く時代さようなら!
public void Show(string arg)
{
Console.WriteLine(arg + arg);
}
}
これにより、積極的なンターフェスの抽出と
契約の記述が促されます(不純動機ドリブン)
- 24. 嬉しいこと3
静的チェッカーでTester-Doerパターンを安全に
// こんなどうでもいいクラスがあるとして
public class ToaruClass
{
int value;
public bool IsReadOnly { get; private set; }
public void SetValue(int value)
{
Contract.Requires(!IsReadOnly);
this.value = value;
}
}
var toaru = new ToaruClass();
// IsReadOnlyをチェックしていないのでunproven
toaru.SetValue(100);
// こう書けばSafe
if (!toaru.IsReadOnly) toaru.SetValue(100);
- 25. Requiresの基本
Requiresで検証する要素は外部から見えないと、バ
ナリリラターを通りません
public class ToaruClass
{
int value;
private bool isReadOnly;
public ToaruClass(bool isReadOnly)
{
this.isReadOnly = isReadOnly;
}
public void SetValue(int value)
{
// isReadOnlyが外から不可視なのでダメ
Contract.Requires(!isReadOnly);
this.value = value;
}
}
- 27. Requiresの基本 Part2
Requires内で使えるメソッドはPureなもののみ
警告なので実行は出来なくはない
// Pureを付けないと警告が!
[Pure]
public static bool IsNull(string arg)
{
return arg == null;
}
public void Hoge(string arg)
{
Contract.Requires(!IsNull(arg));
}
Pure、つまり副作用ナシということ
String.IsNullOrEmptyなど当然Pure属性ついてます
Pureかどうかは自己申告制だったり(非Pureなもので
も付けること自体は可能、勿論それはダメですよ)
- 28. 嬉しくないこと
静的チェッカーは契約の連鎖で成り立っているの
で、契約されてないラブラリが混じると警告祭
りになって鬱陶しい
そういう場合はContract.Assumeで、契約済みを擬
態していくのだけど数が多いと心が折れる、だけ
じゃなくコードが汚れて可読性悪化の一方に
Typeの一部とかExpressionの一部とか、契約済みの
はずの標準ラブラリの中にも上手く動かないの
がチラホラ
- 29. 例えばこんなunproven
// これは静的チェッカでunproven行き
var func = typeof(Func<,>);
var genFunc = func.MakeGenericType(typeof(int), typeof(int));
// 警告を元に、こうAssumeすればいいんですがなんというかかんというか
var func = typeof(Func<,>);
Contract.Assume(func.IsGenericTypeDefinition);
Contract.Assume(func.GetGenericArguments().Length == 2);
var genFunc = func.MakeGenericType(typeof(int), typeof(int));
- 30. Unproven Hell
// (object x) => (object)((T)x).name
static Func<object, object> CreateGetValue(Type type, string name)
{
Contract.Requires<ArgumentNullException>(type != null);
Contract.Requires<ArgumentNullException>(name != null);
// Expression.Unboxに事後条件非nullの契約がないため
// Expression.PropertyOrFieldの引数が求めるrequires expr != null の検証に失敗する
var x = Expression.Parameter(typeof(object), "x");
var func = Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.PropertyOrField(
(type.IsValueType)
? Expression.Unbox(x, type)
: Expression.Convert(x, type),
name),
typeof(object)),
x);
return func.Compile();
}
- 31. どういうこと?
Expressionも基本的には契約されているんですが、
Expression.UnboxとかExpression.Assignと
か、.NET4で新しく追加されたものはあまり契約さ
れてないみたい
なので山崎春のunproven祭り
Expressionは基本的に引数に突っ込んで式としてツ
リー上に組み立てていくものなので、Assumeする
のが難しい
もしAssumeするなら、全部バラして変数にしてから
組み立てなければならないけど、それはない
- 34. その他
.NET4標準に入っているContractsラブラリの他に、
幾つか追加の属性が C:¥Program Files
(x86)¥Microsoft¥Contracts¥Languages¥CSharp に
ある(.csフゔルぽん置き)
使い方の詳細はマニュゕルに載ってる
Microsoft
Researchで開発されている自動パラメタ
ラズドテストPexに対してContractsが記述されて
いると、有効な自動生成パラメータが生成できる
ようになる
http://research.microsoft.com/en-us/projects/pex/
- 35. まとめ
メリットを幾つかあげましたが、忘れてはならな
い基本的なことは、「事前・事後・不変」の契約
が出来るということ
でも、堅苦しい理屈だけじゃなく、目で見て分か
る実用的な便利さを提供してくれるのはいいね!
if-then-throwを撲滅してくれるというだけでも十
分嬉しいなって
まずはそこからで、徐々に高度にステップゕップ
すればいいんじゃないかな
Expressで使えないのが痛い&Premium以上でない
と静的チェッカーが使えないのが大変痛いので、
将来は何とかして欲しいと切実に願う