如何VB.NET编译器来选择要运行的扩展超载?(How does VB.NET compiler c

2019-06-27 23:41发布

有一个有趣的古怪 - 以为有人也许能提供帮助。

这冒出来的一些有趣的空类型从这个问题:

如何检查对象是否为空?

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

我预计到ISNULLABLE两个通话将被视为由编译器一样,但事实并非如此。 扩展方法调用使用不同的过载到正常的方法调用,即使参数“a”是不变的。

我的问题是为什么呢? 是什么让编译器改变两个电话之间的心灵?

FTR:我们正在使用Visual Studio 2010,.NET Framework 4中。

Answer 1:

认为这是一个错误,或者至少是VB.NET的“功能”。 (我只是不知道哪个VB.NET或C#的是错误的。)

我曾尝试在LINQPad 4(因为这是我有我使用的机器上)和C#我得到了False两种结果,对于不同的是每值类型和枚举Nullable类型,当然。

而对于VB.NET我得到的FalseTrue为所有的值类型和枚举,除了Nullable类型,和ValueType[Enum]这回FalseFalse ,因为你不能有一个ValueType?[Enum]? 。 随着Option Strict OffObject会导致后期绑定,并在运行时查找或者过载会失败,但第二个结果是False也因为你不能有Object?

为了完整起见, Nullable类型返回TrueTrue为这两种语言的预期。

那C#是做一些不同的东西(假设我的测试是正确的),这一事实证实了参考C#“更好的转换”检查是错误的(或者被误读至少 - 在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");
    }
}


Answer 2:

过载2只会工作作为明确定义可为空的(T)的的一个扩展。 例如:

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

这是因为扩展方法扩展的类型(或碱型),在这种情况下是可空(的T)。 调用a.IsNullable()将永远不会调用过载2.这是比较容易的部分弄清楚。 这意味着真正的问题是为什么会超载2被调用,而不是过载1作为标准重载方法调用。

CLR将确定哪些过载到通过执行“使用更好的转换 ”的检查,其中,它隐含转换传递给所述参数(一个或多个)的类型的值(一个或多个)中的过载的方法来定义和然后再往规则清单确定要使用的最佳方法。

从MSDN更好的转换文章:

如果S是T1,C1是更好的转换。

如果S是T2,C2是更好的转换。

Puting此代码到Visual Studio将您显示过载2是更好的转换,因为一个整数(S)是隐式转换可为空的(整数的)一个(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)


文章来源: How does VB.NET compiler choose which extension overload to run?