【问题标题】:How does VB.NET compiler choose which extension overload to run?VB.NET 编译器如何选择运行哪个扩展重载?
【发布时间】:2012-09-07 13:58:26
【问题描述】:

有一个有趣的怪事 - 认为有人可以提供帮助。

这个问题的可空类型很有趣:

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

我希望对 IsNullable 的两个调用都会被编译器同等对待,但事实并非如此。即使参数“a”没有改变,扩展方法调用使用与普通方法调用不同的重载。

我的问题是为什么?是什么让编译器在两次调用之间改变主意?

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

【问题讨论】:

  • 您的问题不清楚“您会认为在 Test sub 中对 IsNullable 的两次调用都会导致使用相同的重载,实际上它们每个都使用不同的重载。”
  • 要点 我在我的代码中添加了一些 cmets。希望这能让事情变得清晰。

标签: .net vb.net extension-methods overloading


【解决方案1】:

Overload 2 只能作为显式定义的 Nullable(of T) 的扩展。例如:

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

这是因为扩展方法扩展了类型(或基类型),在本例中为 Nullable(of T)。调用 a.IsNullable() 永远不会调用重载 2。这是很容易弄清楚的部分。这意味着真正的问题是为什么要调用重载 2 而不是重载 1 作为标准重载方法调用。

CLR 将通过执行“Better Conversion”检查来确定要使用的重载,其中它隐式地将传入的值转换为重载方法中定义的参数的类型,然后继续执行用于确定最佳使用方法的规则清单。

来自 MSDN 更好的转换文章:

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

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

将此代码放入 Visual Studio 将显示重载 2 是更好的转换,因为整数 a (S) 是 a (T2) 的隐式转换的 Nullable(of Integer) 版本。

    ' 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)

【讨论】:

  • 这不是 CLR 决定的,它是 VB.NET 编译器,您的链接是 C# 版本。尽管如此,它可能比equivalent VB.NET documentation 更具可读性。
  • @MarkHurd,关于编译器的要点。谢谢你的澄清。
  • BTW 与ValueType 比较的问题是没有为ValueType 定义==,而不是无法进行隐式转换。
【解决方案2】:

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

我已经在 LINQPad 4 中尝试过(因为这就是我正在使用的机器上的东西),对于 C#,我得到了 False 的两个结果,对于除 Nullable 类型之外的每个值类型和枚举,当然。

而对于 VB.NET,我得到所有值类型和枚举的 FalseTrueNullable 类型除外,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");
    }
}

【讨论】:

  • 顺便说一句,请注意以任一语言引用 ValueType? 时的错误 :-)
  • 我注意到这在 VB.NET 14/C# 6 (VS2015) 中没有改变。所以我假设微软已经决定这是两种语言在这里不同的一个特性。 (我还注意到上述错误消息的 VB.NET 版本信息量更大,但两者仍然觉得“有趣”。)
  • 不,我现在相当确定它仍然是 C# 错误:如果已定义,C# 会选择 IsNullable(this int? obj) 重载(当然 VB.NET 也是如此)。 (当T? 重载仍然可用时,两种语言都不会称其为模棱两可。)
猜你喜欢
  • 2013-04-09
  • 2017-02-25
  • 2012-04-09
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 2014-06-02
  • 1970-01-01
  • 2023-03-29
相关资源
最近更新 更多