7

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

这是来自这个问题的可空类型的一些乐趣:

如何检查一个对象是否可以为空?

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。

4

2 回答 2

2

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

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

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

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

来自 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)
于 2012-09-07T20:45:52.547 回答
0

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

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

False而对于 VB.NET,我得到了True所有值类型和枚举的 and,除了Nullable类型,ValueTypeand [Enum]which return FalseFalse因为你不能有ValueType?or [Enum]?。使用Option Strict Off,Object会导致后期绑定,并且在运行时无法找到任一重载,但第二个结果False也是因为您不能拥有Object?.

为了完整起见,Nullable类型返回True,True两种语言都符合预期。

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");
    }
}
于 2012-09-08T15:29:33.153 回答