基礎からのCode Contracts

15,651 views

Published on

C#ユーザー会セッション資料

Published in: Technology
0 Comments
18 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
15,651
On SlideShare
0
From Embeds
0
Number of Embeds
4,821
Actions
Shares
0
Downloads
48
Comments
0
Likes
18
Embeds 0
No embeds

No notes for slide

基礎からのCode Contracts

  1. 1. 基礎からのCode Contracts @neuecc – 2011/5/23
  2. 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使った
  3. 3. First Step
  4. 4. Code Contracts .NET4から標準搭載された? mscorlibにSystem.Diagnostics.Contracts (主に)その中のContractクラスのメソッド群
  5. 5. 何か動かないよ? よくあるnullチェックをしてみようと思った  Contract.Requiresは事前条件  引数がnullだったら契約違反という感じにしたいstatic void Hoge(string arg){ Contract.Requires(arg != null);} が、実行しても無反応 Conditional属性がついているのでコンパ゗ル時に 消える(条件付きメソッド、DEBUGとかでお馴染み)  条件はCONTRACTS_FULL(但し自分で足す意味はない)
  6. 6. 何か動かないよ? Part2 よくあるnullチェックをしてみようと思った again  Contract.Requires<TException>も事前条件  引数がnullだったら契約違反で例外ぶん投げたいstatic void Hoge(string arg){ Contract.Requires<ArgumentNullException>(arg != null);} が、変なゕサートが飛ぶ そしてゕプリは強制終了 リラ゗ターがmustだと?
  7. 7. つまるところ Code Contractsの利用にはリラ゗ターが必要 最終的な配布物はコンパ゗ラオプションで契約用コードを 取り除く。従って実行効率にも影響しない。 http://ja.wikipedia.org/wiki/契約プログラミング 契約は取り除かれなければならない そのためにはラ゗ブラリだけでは不可能で、コン パ゗ル時にバ゗ナリを弄る必要がある 契約の実現のため、現状はバ゗ナリ改変している  真に標準搭載されたと言えるのはリラ゗ターがコン パ゗ラと統合された時かもね
  8. 8. Code Contractsの構成物 必須  Contractクラスなどコードに記述するマーカー  .NET 4で現状標準搭載されているのはこれだけ  バ゗ナリリラ゗ター(ccrewrite.exe) オプション  参照ラ゗ブラリ生成(ccrefgen.exe)  ドキュメント生成(ccdocgen.exe)  静的チェッカー(cccheck.exe)  cccheckはPremium Editionのみ  静的チェックなしの場合は、例外orゕサートを投げる実 行時チェックという形になる
  9. 9. Get Ready to Contracts
  10. 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. 11. プロジェクトのプロパテゖ Contractsタブが追加されてる に、Code チェックボックスをオンにすると各機能が有効に パラメータがいっぱいあって困る?  マニュゕルを見れば勿論、説明がある  日本語で?zeclさんのスラ゗ドを見よう!  http://d.hatena.ne.jp/zecl/20110213/p2
  12. 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. 13. 事前条件の違い EndContractBlockはレガシー環境用  バ゗ナリリラ゗ターがある環境が前提なら不要 Assembly Modeの選択  Requires, Requires<TE>はStandard Contract  EndContractBlockを使う場合はCustom Parameter 無印と<TE>ではリラ゗ト時に残るレベルが違う  無印の場合はReleaseRequiresでは除去される DebugはFull、ReleaseではPreまたはReleaseを推奨
  14. 14. 事後・不変・゗ンターフェ゗ス 事後 : Contract.Ensures  戻り値を表すContract.Result<T>とセットで使うこと が多い 不変 : Contract.Invariant  ContractInvariantMethod属性とセットで  cimコードスニペットを使えば展開される ゗ンターフェ゗スへの契約  書くのがヘンテコで面倒くさい  cintfコードスニペットを使えば展開される
  15. 15. Marriage with IntelliSense
  16. 16. 動かしたけど嬉しさ少なめ? 静的チェッカなしだと、どうも地味  Premiumの人なら関係ないですねShit! そんな物足りなさを感じるゕナタにVisualな贈り物 VS拡張:Code Contracts Editor Extensions http://visualstudiogallery.msdn.microsoft.com/85f0aa38 -a8a8-4811-8b86-e7f0b8d8c71b 契約がIntelliSenseに表示される! FreeなのでVS Professionalの人でもOK
  17. 17. おや、標準ラ゗ブラリの様子が .NET4からBCLも契約済み  そういう意味では標準搭載と言えなくもない
  18. 18. 使い方 標準ラ゗ブラリは何もしなくても表示される 自作の契約はReference Assemblyを作る必要がある Reference Assemblyはクラスラ゗ブラリなど、契約 が除去されたリリース用バ゗ナリを参照する他の ラ゗ブラリが契約情報を参照したい場合に必要 (但し、決してリラ゗ト後のバ゗ナリに契約を再 度埋め込めれるわけではない)
  19. 19. 但し制限も色々あり コンストラクタは表示されません ジェネリックメソッドは表示されません  Enumerable.Rangeは表示されるのにRepeatは表示さ れなかったりしてるのが確認できます  つまるところLINQのメソッドは全滅 yieldが含まれると表示されません dynamicが含まれると表示されません よく落ちます(落ちたらVS再起動まで復活しない)Editor Extensionsに関してはアルファ版だと思って暖かく見守りましょう
  20. 20. Merit and Demerit
  21. 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. 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. 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. 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をチェックしていないのでunproventoaru.SetValue(100);// こう書けばSafeif (!toaru.IsReadOnly) toaru.SetValue(100);
  25. 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; }}
  26. 26. なんでなんで? Requires、事前条件はメソッド呼び出し側が、正し い呼び出しが可能かの責任を負う必要がある、つ まり外から検証可能でないとならない 逆にEnsures、事後条件が正しく成立するかはメ ソッド側の責任なので、メソッド内部できちんと Ensuresの条件が満たせる必要がある
  27. 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. 28. 嬉しくないこと 静的チェッカーは契約の連鎖で成り立っているの で、契約されてないラ゗ブラリが混じると警告祭 りになって鬱陶しい そういう場合はContract.Assumeで、契約済みを擬 態していくのだけど数が多いと心が折れる、だけ じゃなくコードが汚れて可読性悪化の一方に Typeの一部とかExpressionの一部とか、契約済みの はずの標準ラ゗ブラリの中にも上手く動かないの がチラホラ
  29. 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. 30. Unproven Hell// (object x) => (object)((T)x).namestatic 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. 31. どういうこと? Expressionも基本的には契約されているんですが、 Expression.UnboxとかExpression.Assignと か、.NET4で新しく追加されたものはあまり契約さ れてないみたい なので山崎春のunproven祭り Expressionは基本的に引数に突っ込んで式としてツ リー上に組み立てていくものなので、Assumeする のが難しい  もしAssumeするなら、全部バラして変数にしてから 組み立てなければならないけど、それはない
  32. 32. そして平穏が訪れる// 面倒くさくて耐え切れない時は静的検証オフ属性をつけてやる[ContractVerification(false)]static Func<object, object> Create(Type type, string name){ // (中略)} Contract.Ensures(Contract.Result<T>() != null); がど れだけ大事かが身にしみて分かる  しかし定型句すぎて面倒くさいのは事実……  cenコードスニペットがあるとはいえ
  33. 33. Conclusion
  34. 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. 35. まとめ メリットを幾つかあげましたが、忘れてはならな い基本的なことは、「事前・事後・不変」の契約 が出来るということ でも、堅苦しい理屈だけじゃなく、目で見て分か る実用的な便利さを提供してくれるのはいいね! if-then-throwを撲滅してくれるというだけでも十 分嬉しいなって まずはそこからで、徐々に高度にステップゕップ すればいいんじゃないかな Expressで使えないのが痛い&Premium以上でない と静的チェッカーが使えないのが大変痛いので、 将来は何とかして欲しいと切実に願う

×