Upgrade to Pro — share decks privately, control downloads, hide ads and more …

基礎からの Code Contracts

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

基礎からの Code Contracts

Avatar for Yoshifumi Kawai

Yoshifumi Kawai

May 23, 2011
Tweet

More Decks by Yoshifumi Kawai

Other Decks in Technology

Transcript

  1.  Twitter => @neuecc  Blog => http://neue.cc/  HNは"neuecc"

    読むときは“のいえ”で ⚫ ドメイン繋いだだけで特に意味はなく発音不能のた め(ccは声に出しにくいのでスルーという適当対応)  Microsoft MVP for Visual C#(2011/4-)  公開してるライブラリとか ⚫ linq.js ⚫ DynamicJson ⚫ Chaining Assertion ⚫ DbExecutor <- (ちょっとだけ)Code Contracts使った Profile
  2.  よくあるnullチェックをしてみようと思った ⚫ Contract.Requiresは事前条件 ⚫ 引数がnullだったら契約違反という感じにしたい static void Hoge(string arg)

    { Contract.Requires(arg != null); }  が、実行しても無反応  Conditional属性がついているのでコンパイル時に 消える(条件付きメソッド、DEBUGとかでお馴染み) ⚫ 条件はCONTRACTS_FULL(但し自分で足す意味はない) 何か動かないよ?
  3.  よくあるnullチェックをしてみようと思った again ⚫ Contract.Requires<TException>も事前条件 ⚫ 引数がnullだったら契約違反で例外ぶん投げたい static void Hoge(string

    arg) { Contract.Requires<ArgumentNullException>(arg != null); }  が、変なアサートが飛ぶ  そしてアプリは強制終了  リライターがmustだと? 何か動かないよ? Part2
  4.  Code Contractsの利用にはリライターが必要 最終的な配布物はコンパイラオプションで契約用コードを 取り除く。従って実行効率にも影響しない。 http://ja.wikipedia.org/wiki/契約プログラミング  契約は取り除かれなければならない  そのためにはライブラリだけでは不可能で、コン

    パイル時にバイナリを弄る必要がある  契約の実現のため、現状はバイナリ改変している ⚫ 真に標準搭載されたと言えるのはリライターがコン パイラと統合された時かもね つまるところ
  5.  必須 ⚫ Contractクラスなどコードに記述するマーカー ⚫ .NET 4で現状標準搭載されているのはこれだけ ⚫ バイナリリライター(ccrewrite.exe) 

    オプション ⚫ 参照ライブラリ生成(ccrefgen.exe) ⚫ ドキュメント生成(ccdocgen.exe) ⚫ 静的チェッカー(cccheck.exe) ⚫ cccheckはPremium Editionのみ ⚫ 静的チェックなしの場合は、例外orアサートを投げる実 行時チェックという形になる Code Contractsの構成物
  6.  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では使えない  静的チェッカーの有無も大きなところ ⚫ 契約の正しさが実行時じゃないと確認出来ないとい うのは、何が正しいのか分からない初学者にとって 学習が困難になる Code Contractsのインストール
  7.  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); 事前条件
  8.  EndContractBlockはレガシー環境用 ⚫ バイナリリライターがある環境が前提なら不要  Assembly Modeの選択 ⚫ Requires, Requires<TE>はStandard

    Contract ⚫ EndContractBlockを使う場合はCustom Parameter  無印と<TE>ではリライト時に残るレベルが違う ⚫ 無印の場合はReleaseRequiresでは除去される  DebugはFull、ReleaseではPreまたはReleaseを推奨 事前条件の違い
  9.  事後 : Contract.Ensures ⚫ 戻り値を表すContract.Result<T>とセットで使うこと が多い  不変 :

    Contract.Invariant ⚫ ContractInvariantMethod属性とセットで ⚫ cimコードスニペットを使えば展開される  インターフェイスへの契約 ⚫ 書くのがヘンテコで面倒くさい ⚫ cintfコードスニペットを使えば展開される 事後・不変・インターフェイス
  10.  静的チェッカなしだと、どうも地味 ⚫ Premiumの人なら関係ないですねShit!  そんな物足りなさを感じるアナタにVisualな贈り物  VS拡張:Code Contracts Editor

    Extensions http://visualstudiogallery.msdn.microsoft.com/85f0aa38 -a8a8-4811-8b86-e7f0b8d8c71b  契約がIntelliSenseに表示される!  FreeなのでVS Professionalの人でもOK 動かしたけど嬉しさ少なめ?
  11.  コンストラクタは表示されません  ジェネリックメソッドは表示されません ⚫ Enumerable.Rangeは表示されるのにRepeatは表示さ れなかったりしてるのが確認できます ⚫ つまるところLINQのメソッドは全滅 

    yieldが含まれると表示されません  dynamicが含まれると表示されません  よく落ちます(落ちたらVS再起動まで復活しない) Editor Extensionsに関してはアルファ版だと思って 暖かく見守りましょう 但し制限も色々あり
  12.  引数名を文字列で指定しなくてもいい ⚫ リライターが埋め込んでくれるから ⚫ コードスニペット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"); 嬉しいこと1
  13.  インターフェイスに契約すると、それを実装する ものへは何も書かなくても自動で埋め込まれる // こうしてインターフェイスへの契約を作ると(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); } } 嬉しいこと2
  14. 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); } }  これにより、積極的なインターフェイスの抽出と 契約の記述が促されます(不純動機ドリブン) それはとっても嬉しいなって
  15.  静的チェッカーで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); 嬉しいこと3
  16.  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; } } Requiresの基本
  17.  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なもので も付けること自体は可能、勿論それはダメですよ) Requiresの基本 Part2
  18. // これは静的チェッカで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)); 例えばこんなunproven
  19. // (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(); } Unproven Hell
  20. // 面倒くさくて耐え切れない時は静的検証オフ属性をつけてやる [ContractVerification(false)] static Func<object, object> Create(Type type, string name)

    { // (中略) }  Contract.Ensures(Contract.Result<T>() != null); がど れだけ大事かが身にしみて分かる ⚫ しかし定型句すぎて面倒くさいのは事実…… ⚫ cenコードスニペットがあるとはいえ そして平穏が訪れる
  21.  .NET4標準に入っているContractsライブラリの他に、 幾つか追加の属性が C:¥Program Files (x86)¥Microsoft¥Contracts¥Languages¥CSharp に ある(.csファイルぽん置き) ⚫ 使い方の詳細はマニュアルに載ってる

     Microsoft Researchで開発されている自動パラメタ ライズドテストPexに対してContractsが記述されて いると、有効な自動生成パラメータが生成できる ようになる http://research.microsoft.com/en-us/projects/pex/ その他
  22.  メリットを幾つかあげましたが、忘れてはならな い基本的なことは、「事前・事後・不変」の契約 が出来るということ  でも、堅苦しい理屈だけじゃなく、目で見て分か る実用的な便利さを提供してくれるのはいいね!  if-then-throwを撲滅してくれるというだけでも十 分嬉しいなって

     まずはそこからで、徐々に高度にステップアップ すればいいんじゃないかな  Expressで使えないのが痛い&Premium以上でない と静的チェッカーが使えないのが大変痛いので、 将来は何とかして欲しいと切実に願う まとめ