Got an interesting oddity - thought someone might be able to help.
This came out of some fun with nullable types from this question:
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
I would expect that both calls to IsNullable would be treated the same by the compiler, but that is not the case. The extension method call uses a different overload to the normal method call even though the argument "a" is unchanged.
My question is why? What makes the compiler change its mind between the two calls?
FTR: We are using Visual Studio 2010, .NET Framework 4.
Extension methods enable developers to add custom functionality to data types that are already defined without creating a new derived type. Extension methods make it possible to write a method that can be called as if it were an instance method of the existing type.
The extension methods have a special symbol in intellisense of the visual studio, so that you can easily differentiate between class methods and extension methods.
Extension methods are defined as static methods but are called by using instance method syntax. Their first parameter specifies which type the method operates on. The parameter is preceded by the this modifier.
In C#, the extension method concept allows you to add new methods in the existing class or in the structure without modifying the source code of the original type and you do not require any kind of special permission from the original type and there is no need to re-compile the original type.
Overload 2 will ONLY work as an extension on explicitly defined Nullable(of T)'s. For example:
Dim y As New Nullable(Of Integer)
y.IsNullable()
This is because extension methods extend the type (or a base type), which in this case is Nullable(of T). Calling a.IsNullable() will never call overload 2. That's the easy part to figure out. This means the real question is why would overload 2 be called instead of overload 1 as a standard overloaded method call.
The CLR will determine which Overload to use by performing a "Better Conversion" check, where it implicitly converts the value(s) passed in to the type of the parameter(s) defined in the overloaded methods and then go down a checklist of rules to determine the best method to use.
From the MSDN Better Conversion Article:
If S is T1, C1 is the better conversion.
If S is T2, C2 is the better conversion.
Puting this code into Visual Studio will shows you that Overload 2 is the better conversion because the integer a (S) is the implicitly converted Nullable(of Integer) version of a (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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With