2012-09-07 15 views
7

Mam ciekawe osobliwości - myślałem, że ktoś może ci pomóc.W jaki sposób kompilator VB.NET wybiera, które rozszerzenie przeciążenia należy uruchomić?

Ten wyszedł trochę zabawy z pustych typów z tym pytanie:

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 

chciałbym oczekiwać, że oba połączenia z IsNullable byłyby traktowane tak samo przez kompilator, ale to nie jest walizka. Wywołanie metody extension używa innego przeciążenia do normalnego wywołania metody, mimo że argument "a" pozostaje niezmieniony.

Moje pytanie brzmi: dlaczego? Co sprawia, że ​​kompilator zmienia zdanie między tymi dwoma wywołaniami?

FTR: Jesteśmy przy użyciu Visual Studio 2010, .NET Framework 4.

+0

Twoje pytanie nie jest jasne „Można by pomyśleć, oba połączenia do IsNullable w podpunktach testowym skutkowałoby tym samym przeciążenia są używane, rzeczywiście każdy z nich użyj innego. " –

+0

Punkt zaczerpnięty Dodałem komentarze do mojego kodu. Mam nadzieję, że to wyjaśnia. –

Odpowiedz

0

myślę to jest błąd, albo przynajmniej "cecha" VB.NET. (Po prostu nie jestem pewien, który z VB.NET lub C# jest źle.)

Próbowałem w LINQPad 4 (ponieważ to, co mam na maszynie używam) i dla C# mam False dla obu wyników, dla każdego typu wartości i wyliczenia, z wyjątkiem typów Nullable, oczywiście.

Zważywszy na VB.NET uzyskać False i True dla wszystkich typów wartości i teksty stałe, z wyjątkiem Nullable typów i ValueType i [Enum] które zwracają False, False, ponieważ nie można mieć ValueType? lub [Enum]?. Z numerem Option Strict Off, Object powoduje późne wiązanie i kończy się niepowodzeniem w czasie wykonywania, aby zlokalizować przeciążenie, ale drugi wynik to False, ponieważ nie można uzyskać Object?.

Dla kompletności, typy zwracają True, True dla obu języków zgodnie z oczekiwaniami.

Fakt, że C# robi coś innego (zakładając, że mój test jest poprawny) potwierdza, że ​​odwołanie do sprawdzenia C# "Lepsza konwersja" jest nieprawidłowe (lub przynajmniej błędne odczytanie - w tym C# nie robi tego, co jest interpretowane jako dlaczego VB.NET robi to, co robi).

Jednak zgadzam się, że problem jest prawdopodobnie związany z niejawna konwersja do Nullable(Of T) istniejącej i jakoś będącego wyższy priorytet na niejawna konwersja do ValueType.

Oto mój LINQPad 4 „Query” (C# Program):

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 Zwróć uwagę na błąd związany z 'ValueType?' W obu językach :-) –

+0

Zauważam, że to się nie zmieniło w VB.NET 14/C# 6 (VS2015). Zakładam więc, że Microsoft zdecydował, że jest to funkcja, której dwa języki różnią się tutaj. (Zauważam też, że wersja VB.NET wyżej wymienionych komunikatów o błędach jest bardziej pouczająca, ale oba nadal wydają się "zabawne".) –

+0

Nie, jestem teraz dość pewny, że to nadal błąd C#: C# wybiera 'IsNullable (to int ? obj) 'przeciążenie, jeśli jest zdefiniowane (podobnie jak VB.NET oczywiście). (I żaden język nie nazywa tego niejednoznacznym, gdy przeciążenie 'T?' Jest nadal dostępne.) –

2

przeciążeniem 2 będzie działać tylko jako przedłużenie na wyraźnie określone pustych (z t) 's. Np

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

Wynika to rozszerzenie metod przedłużenia typu (lub typu zasad), który w tym przypadku jest Nullable (T). Wywołanie metody a.InsNullable() nigdy nie wywoła przeciążenia 2. To łatwe do wymyślenia. Oznacza to, że prawdziwe pytanie brzmi, dlaczego zamiast przeciążenia 1 wywołanoby przeciążenie 1 jako standardowe przeciążone wywołanie metody.

CLR będzie określić, które Przeciążenie w użyciu przez wykonując „Better Conversion” sprawdzić, gdzie jest niejawnie konwertuje wartość (y) przeszedł do typu parametru (ów) określone w przeciążonych metod, a następnie przejść w dół lista kontrolna reguł w celu określenia najlepszej metody użycia.

Z artykułu MSDN Lepsze konwersji:

Jeśli S jest T1, C1 jest lepsza konwersja.

Jeśli S jest T2, C2 oznacza lepszą konwersję.

Puting ten kod z Visual Studio pokazuje, że przeciążenie 2 jest lepsza konwersja ponieważ liczba całkowita a (S) jest niejawnie konwertowane pustych (Integer) wersja (T2).

' 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

To nie CLR określa to, jest to kompilator VB.NET, a twój link do wersji C#. Niemniej jednak może być bardziej czytelny niż [odpowiednik dokumentacji VB.NET] (http://msdn.microsoft.com/en-us/library/aa712031.aspx). –

+0

@MarkHurd, dobry punkt kompilatora. Dziękuję za to wyjaśnienie. – N0Alias

+0

BTW Problem z porównaniem z "ValueType" polega na tym, że nie istnieje '==' zdefiniowany dla 'ValueType', a nie, że nie można wykonać niejawnej konwersji. –