2012-09-07 4 views
7

興味深い奇妙なことをしました - 誰かを助けることができるかもしれないと考えました。私はISNULLABLEの両方の呼び出しは、コンパイラによって同じように扱われるだろうが、それがないことを期待しVB.NETコンパイラはどの拡張オーバーロードを実行するかをどのように選択しますか?

How to check if an object is nullable?

Option Strict On 

Module Test 
    ' Call this overload 1 
    <Extension()> 
    Function IsNullable(obj As ValueType) As Boolean 
    Return False 
    End Function 

    ' Call this overload 2 
    <Extension()> 
    Function IsNullable(Of T As {Structure})(obj As Nullable(Of T)) As Boolean 
    Return True 
    End Function 

    Sub Test() 
    ' a is an integer! 
    Dim a As Integer = 123 

    ' calling IsNullable as an extension method calls overload 1 and returns false 
    Dim result1 As Boolean = a.IsNullable() 

    ' calling IsNullable as method calls overload 2 and returns true 
    Dim result2 As Boolean = IsNullable(a) 

    ' why? surely the compiler should treat both those calls as equivalent 
    End Sub 
End Module 

は、この、この質問からNULL値可能なタイプでいくつかの楽しみから出てきましたケース。拡張メソッド呼び出しは、引数 "a"が変更されていなくても、通常のメソッド呼び出しに対して異なるオーバーロードを使用します。

私の質問はなぜですか?コンパイラは2つの呼び出しの間にその心を変えますか?

FTR:我々のVisual Studio 2010を使用している、.NET Frameworkの4

+0

あなたの質問はあなたがテストサブでISNULLABLEの両方の呼び出しが使用されている同じ過負荷になると思うだろう」明確ではないが、実際に彼らそれぞれが別のものを使用してください。 –

+0

取得ポイント私は自分のコードにいくつかのコメントを追加しました。これが物事をはっきりさせることを望みます。 –

答えて

0

私はこれはバグ、または少なくともVB.NET "機能" であるだと思い。 (VB.NETやC#のどれが間違っているのか分かりません)

私はLINQPad 4で試しています(使用しているマシン上にあるためです)。両方の結果に対して、もちろん、Nullable型を除くすべての値型と列挙型に対してです。

あなたがValueType?[Enum]?を持つことはできませんので、私はFalseFalseを返すNullable種類、およびValueType[Enum]を除き、すべての値の型と列挙型のためFalseTrueを取得VB.NET用のに対し。 Option Strict Offの場合、Objectはレイトバインディングを引き起こし、実行時にどちらかのオーバーロードを検出できませんが、Object?を持つことができないため、2番目の結果はFalseにもなります。

Nullableタイプは、期待どおりに両方の言語のTrue,Trueを返します。

私のテストが正しいと仮定して、C#が何か別のことをしているという事実は、C#の "Better Conversion"チェックへの参照が間違っている(または間違っている - 少なくともC#では何が解釈されていないなぜVB.NETがそれをやっているのか)。

しかし、この問題はおそらくNullable(Of T)への暗黙的な変換に関連しており、何とか暗黙的な変換であるValueTypeへの優先順位に関係していると私は同意します。

ここに私LINQPad 4「クエリ」(C#プログラム)です:

void Main() 
{ 
    Test.test(); 
} 

// Define other methods and classes here 
static class Test 
{ 
    static bool IsNullable(this ValueType obj) 
    { 
     return false; 
    } 

    static bool IsNullable<T>(this T? obj) where T:struct 
    { 
     return true; 
    } 

    public static void test() 
    { 
     int x = 42; 

     bool result1 = x.IsNullable(); 
     bool result2 = IsNullable(x); 

     result1.Dump("result1"); 
     result2.Dump("result2"); 
    } 
} 
+0

BTWいずれかの言語で 'ValueType? 'を参照するときのエラーをメモしてください。 –

+0

これはVB.NET 14/C#6(VS2015)では変更されていません。だから、マイクロソフトでは、ここでは2つの言語が異なるという特徴があると判断したと仮定します。 (上記のエラーメッセージのVB.NETバージョンはより有益ですが、両方ともまだ面白いと感じています) –

+0

いいえ、私は今でもかなりC#バグだと確信しています。C#はIsNullableを選択します?obj) 'オーバーロードが定義されている場合(もちろんVB.NETと同様です)。 (また、 'T?'オーバーロードがまだ利用可能なときには、どちらの言語もそれをあいまいと呼んでいません)。 –

2

が過負荷2は、ONLY(Tの)明示的に定義されたのNullableの上の拡張機能として動作します。たとえば、次の拡張メソッドは、タイプ(または塩基型)この場合、(Tの)NULL可能であり、伸びるので

Dim y As New Nullable(Of Integer) 
    y.IsNullable() 

です。 a.IsNullable()を呼び出すと、オーバーロード2は呼び出されません。これは簡単に把握することができます。つまり、実際の問題は、標準のオーバーロードされたメソッド呼び出しとして、オーバーロード1の代わりにオーバーロード2が呼び出される理由です。

CLRは、オーバーロードされたメソッドで定義されているパラメータの型に渡された値を暗黙的に変換してから、下に移動して、 "Better Conversion"チェックを実行して使用するオーバーロードを判断します。ルールのチェックリストを使用して最適な方法を決定します。

MSDNより良い変換記事から:

SがT1であれば、C1は、より良い変換です。

SがT2の場合、C2はより良い変換です。

このコードをVisual Studioに挿入すると、整数a(S)が(T2)の暗黙的に変換されたNullable(Integer)バージョンなので、オーバーロード2がより良い変換であることがわかります。

' a is an integer! 
    Dim a As Integer = 123 

    Dim objValueType As ValueType = 123 'Or CType(a, ValueType) 
    Dim objNullable As Nullable(Of Integer) = 123 'Or CType(a, Nullable(Of Integer)) 

    'Oh No, a compiler error for implicit conversion done for overload 1! 
    Dim bolValueTypeConversionIsBetter As Boolean = (objValueType = a) 

    'No error as long as Option Strict is off and it will equal True. 
    Dim bolNullableConversionIsBetter As Boolean = (objNullable = a) 
+0

これを決定するのはCLRではなく、VB.NETコンパイラです。リンクはC#バージョンです。それにもかかわらず、[同等のVB.NETのドキュメント](http://msdn.microsoft.com/en-us/library/aa712031.aspx)よりも読みやすいかもしれません。 –

+0

@MarkHurd、コンパイラの良い点。その説明をありがとうございます。 – N0Alias

+0

BTW 'ValueType'との比較の問題は、' ValueType'に '=='が定義されていないことです。暗黙の変換はできません。 –

関連する問題