令人困惑的重载决议

Confusing overload resolution

我有一个 class 继承自另一个 class 以允许使用可为 null 的值。但是当我将它与不可空值一起使用时 ,它无论如何都会为可空值使用重载。

我不明白为什么 class c2 的功能屏蔽了 class c1 在这段代码中的功能:

class c1
{
    public void fn1(int value)
    {
        Console.WriteLine("value {0} is normal", value);
    }
    public void fn1(int? value)
    {
        Console.WriteLine("value {0} is nullable", value);
    }
    public void fn2(int value)
    {
        Console.WriteLine("value {0} is normal", value);
    }
}
class c2: c1
{
    public void fn2(int? value)
    {
        Console.WriteLine("value {0} is nullable", value);
    }
}

class Program
{
    static void Main(string[] args)
    {
        c1 o1 = new c1();
        c2 o2 = new c2();
        int val = 10;
        o1.fn1(val);
        o2.fn1(val);
        o2.fn2(val);
        Console.ReadLine();
    }
}

结果是:

value 10 is normal.
value 10 is normal.
value 10 is nullable.

但是 c2 有函数 fn2(int)。

您的功能解析遵循您的 class 层次结构。在您发布的示例中,函数 c2::fn2 与您的调用 o2.fn2(val) 匹配,因为它已在 c2 中明确定义并且编译器可以隐式地将 int 转换为 int?实际上你隐藏了 c1::fn2.

您可以考虑使您的 c1 class 抽象(无实现)或虚拟,并在 c2 中覆盖。

这是我在 Jon Skeet 的博客上找到的。

Inheritance can cause a confusing effect. When the compiler goes looking for instance method overloads, it considers the compile-time class of the "target" of the call, and looks at methods declared there. If it can't find anything suitable, it then looks at the parent class... then the grandparent class, etc. This means that if there are two methods at different levels of the hierarchy, the "deeper" one will be chosen first, even if it isn't a "better function member" for the call.

http://csharpindepth.com/Articles/General/Overloading.aspx

在许多边缘情况下都是如此,没有什么神圣的 "why",这就是 C# 规范的编写方式。他们本可以用其他方式做到这一点,但决定这样做。 https://msdn.microsoft.com/en-us/library/aa691336(v=vs.71).aspx

最相关的部分(来自上面链接的 C# 规范的“7.4.2 重载解决方案”,不同版本的规范在其他部分可能有类似的文本):

the set of candidates for a method invocation does not include ... methods in a base class are not candidates if any method in a derived class is applicable

就像说 Handoko.Chen,在 Jon Skeet 的博客上我们可以找到

The compiler ignores the overriding method in the child.

所以我找到的唯一方法是隐藏继承方法:

class c2: c1 {
    public void fn2(int? value) {
        Console.WriteLine("value {0} is nullable", value);
    }
    public new void fn2(int value) {
        base.fn2(value);
    }
}