none
検証が成功した場合に、引数に関するContract.Ensures() を書く RRS feed

  • 質問

  • 以下のようなメソッドを書いています。

    public bool IsNullableValue(Type t)
    {
        Contract.Requires<ArgumentNullException>(t != null);
    
        if (!t.IsValueType) return false;
        if (!t.IsGenericType) return false;
        if (t.IsGenericTypeDefinition) return false;
        if (t.IsGenericParameter) return false;
        return t.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

    これを使用して、以下のようなコードを書いていますが、

    「t (Nullable<T> であることを確認済み)の型引数がnot null と確認できない」

    とCodeContracts が警告を出力します。

    if (IsNullableValue(t))
    {
        var tNotNull = t.GetGenericArguments()[0];
        // unproven tNotNull != null
    }
    
    IsNullableValue(t) == true の場合、t.GetGenericArguments()[0] != null なので、何とかunproven にならないようにしたいのですが、CodeContracts ではどう書けばよいのでしょうか?
    2014年7月20日 17:42

回答

  • 画像は見れませんでしたが、言いたいことも尋ねたいこともわかります。が、Code Contractsの限界でしょう。

    強いて言えば

    if (IsNullableValue(t))
    {
        var genericArguments = t.GetGenericArguments();
        if(genericArguments.Length != 1)
           throw new Expection();
        var tNotNull = genericArguments[0];
        if(tNotNull == null)
           throw new Exception();
    }

    かなと。Code Contractsが警告しなくてもLengthが1でない可能性だってあります。もっというと戻り値がnullの可能性もありますが…まぁこれはドキュメントを信じてチェックを省略するかどうかというところですね。
    メソッドの切り口が悪いのかもしれません。Nullable<T>がどうかを判断するだけでなくContract込みでTを返すとか…。

    • 回答としてマーク dai_okuno 2014年7月26日 7:47
    2014年7月25日 1:07
  • > メソッドの切り口が悪いのかもしれません。Nullable<T>がどうかを判断するだけでなくContract込みでTを返すとか…。

    これでうまくいきました。

    ・Nullable<T> であれば、Tを取得したい。

    というケースで利用したいので、TryGet××(out value) のパターンにしたところ、Code Contracts でも対応できました。

    [SuppressMessage("Microsoft.Contracts", "Ensures-!Contract.Result<bool>() || Contract.ValueAtReturn<Type>(out notNull) != null")]
    public static bool TryGetNotNullType(this Type nullable, out Type notNull)
    {
        Contract.Requires<ArgumentNullException>(nullable != null);
        Contract.Ensures(!Contract.Result<bool>() || Contract.ValueAtReturn<Type>(out notNull) != null);
    
        if (nullable.IsGenericType && nullable.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            notNull = nullable.GetGenericArguments()[0];
            return true;
        }
        else
        {
            notNull = null;
            return false;
        }
    }
    


    利用側ではこんな感じです。

    var t = typeof(T);
    Type tNotNull;
    if (t.TryGetNotNullType(out tNotNull))
    {
        var im = tNotNull.GetInterface("IFormattable");
    }
    

    warning level == hi でチェックをかけても、警告はでなくなりました。工夫すれば何とかなるものですね。

    ニッチな質問に回答いただき、ありがとうございました。

    • 回答としてマーク dai_okuno 2014年7月26日 7:47
    2014年7月26日 7:47

すべての返信

  • t(つまりType型)にそのようなContractが指定されていない以上、呼び出し側で明示するしかないかと。

    Contract.Assume(tNotNull != null);

    Nullable<T>かどうかの判定ならもう少し違った書き方もできそうに思いますが、そのためにはこの周囲で何をやっているかなども確認しないとわかりませんね。

    • 回答の候補に設定 星 睦美 2014年7月22日 2:28
    2014年7月20日 23:33
  • Contract.Assume をその場に書けば、警告は無くなるのですが、コードの可読性は大幅に低下します。

    こんな感じで、警告を黙らせるコードのせいで、本当に書きたいコードがはっきりしなくなります。

    はっきり書きませんでしたが、質問の意図は、

      Contract.Assume(tNotNull != null); と書かないためには、IsNullableValue(t) をどう実装するべきか?

    ということです。

    2014年7月24日 22:33
  • 画像は見れませんでしたが、言いたいことも尋ねたいこともわかります。が、Code Contractsの限界でしょう。

    強いて言えば

    if (IsNullableValue(t))
    {
        var genericArguments = t.GetGenericArguments();
        if(genericArguments.Length != 1)
           throw new Expection();
        var tNotNull = genericArguments[0];
        if(tNotNull == null)
           throw new Exception();
    }

    かなと。Code Contractsが警告しなくてもLengthが1でない可能性だってあります。もっというと戻り値がnullの可能性もありますが…まぁこれはドキュメントを信じてチェックを省略するかどうかというところですね。
    メソッドの切り口が悪いのかもしれません。Nullable<T>がどうかを判断するだけでなくContract込みでTを返すとか…。

    • 回答としてマーク dai_okuno 2014年7月26日 7:47
    2014年7月25日 1:07
  • > メソッドの切り口が悪いのかもしれません。Nullable<T>がどうかを判断するだけでなくContract込みでTを返すとか…。

    これでうまくいきました。

    ・Nullable<T> であれば、Tを取得したい。

    というケースで利用したいので、TryGet××(out value) のパターンにしたところ、Code Contracts でも対応できました。

    [SuppressMessage("Microsoft.Contracts", "Ensures-!Contract.Result<bool>() || Contract.ValueAtReturn<Type>(out notNull) != null")]
    public static bool TryGetNotNullType(this Type nullable, out Type notNull)
    {
        Contract.Requires<ArgumentNullException>(nullable != null);
        Contract.Ensures(!Contract.Result<bool>() || Contract.ValueAtReturn<Type>(out notNull) != null);
    
        if (nullable.IsGenericType && nullable.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            notNull = nullable.GetGenericArguments()[0];
            return true;
        }
        else
        {
            notNull = null;
            return false;
        }
    }
    


    利用側ではこんな感じです。

    var t = typeof(T);
    Type tNotNull;
    if (t.TryGetNotNullType(out tNotNull))
    {
        var im = tNotNull.GetInterface("IFormattable");
    }
    

    warning level == hi でチェックをかけても、警告はでなくなりました。工夫すれば何とかなるものですね。

    ニッチな質問に回答いただき、ありがとうございました。

    • 回答としてマーク dai_okuno 2014年7月26日 7:47
    2014年7月26日 7:47