2013-02-21 89 views
4

考虑:为什么静态在编译时隐藏重写的方法?

class BaseClass 
{ 
    public virtual void M(int x) 
    { 

    } 
} 

class Derived : BaseClass 
{ 
    public override void M(int x) 
    { 
     base.M(x); 
    } 

    static void M(object x) 
    { 

    } 

    static void Main() 
    { 
     var d = new Derived(); 
     d.M(0); 
    } 
} 

错误:

A member lookup of a name N with K type parameters in a type T is processed as follows:

[...] Members that include an override modifier are excluded from the set [of accessible members named N]

由此我得出这样的结论:

Member 'Derived.M(object)' cannot be accessed with an instance reference; qualify it with a type name instead

在C#4.0规范7.4节(会员查询)第一点读展望覆盖Derived.M不再可访问。相反,编译器必须参考BaseClass.M

但是,这并不能解释为什么添加静态Derived.M突然导致编译错误。编译器现在只能看到静态成员Derived.M,并得出结论说这个成员是无效的调用。如果我删除静态Derived.M,则编译成功。

为什么会发生这种情况?

+0

http://www.akadia.com/services/dotnet_polymorphism.html – MethodMan 2013-02-21 16:08:16

+0

这似乎将对象作为静态方法的参数存在问题。如果我将该参数更改为字符串,它可以正常工作。 – cadrell0 2013-02-21 16:09:00

+7

有趣的是,像Eric Lippert这样的人可能会被迫(或者真的)试图编写可以解决这样问题的编译器,这里的真正教训是不会为编译器创建棘手的问题当不同的名字同样有效时,避免给大量的标识符同名。 – Servy 2013-02-21 16:13:23

回答

7

以下步骤似乎发生,是一起为你的编译器错误的原因:

  • 第1步:由于部分从7.4的。你引用了M的实例版本,只留下了静态的编译器。静态参数类型为object,与int兼容。方法名称也匹配。
  • 步骤2:成员查找完成,因为找到了匹配的方法(正确的名称,兼容的参数),不需要继承链。
  • 第3步:现在只检查方法是实例方法还是静态方法。

我真的不能引用规范的一个单句证明这一点,但也§7.4(会员查询),也不3.5节(前往的交通)谈论static与实例,所以我认为这其实根本就不是在进行成员查询时全部考虑到。从§7.4

相关部分似乎是:

A member lookup of a name N with K type parameters in a type T is processed as follows:

  • First, a set of accessible members named N is determined:
    [...]
    The set consists of all accessible (§3.5) members named N in T, including inherited members and the accessible members named N in object. If T is a constructed type, the set of members is obtained by substituting type arguments as described in §10.3.2. Members that include an override modifier are excluded from the set.

我理解这部分是像上面这样解释的方式:它会返回两个实例方法和静态的一个,然后将删除实例中的一个,因为它有override修饰符。
此时只剩下静态方法。

它关闭与此:

Finally, [...], the result of the lookup is determined:
if the set contains only methods, then this group of methods is the result of the lookup.

所以,结果是静态方法。

显然,只有在您有类层次结构并且其中一个派生类声明具有相同名称和兼容参数的静态方法的情况下,才会出现此问题。

将这样一种静态方法添加到现有的类是一种情况,其中简单地添加某些东西仍然是突破性变化

尽管我很确定你知道如何解决编译器错误,但我仍然会声明它有一个完整的答案:
使用任何基类作为变量的编译时类型。运行时类型仍然可以是派生类型,这不是问题:

BaseClass d   = new Derived(); 
//^      ^
// compile time type  runtime type 
+1

“静态的参数类型是对象,它与int兼容。”。这个。如果我将该参数更改为与int不兼容的内容,我不会收到错误。我尝试过使用各种兼容和不兼容的数据类型,并且所有行为都像预期的那样。 – cadrell0 2013-02-21 16:12:59

+0

我觉得你已经把它钉在第二点上了。 – 2013-02-21 16:42:07

+1

终于找到了。该问题的核心在第7.6.5.1节中定义 - “候选方法集合减少为只包含来自大多数派生类型的方法”。因为'Derived.M'比'BaseClass.M'多得多,所以基类作为一个选项被删除。因此......错误。 – 2013-02-21 21:00:38

4

Daniel的答案是正确的;有关规则的简短摘要是:

  • 方法更派生类中比在派生较少类
  • 压倒一切的方法被认为是其首先声明它们的类方法的方法,总是会考虑更好,而不是覆盖它们的类
  • 解决检查接收方是否是实例并且方法是静态的,检查哪个方法(如果有的话)是“最好的”之前。 (并且在检查泛型类型参数约束之前)。

这最后一条规则有点奇怪,但确实有些道理。请参阅我的评论和nikov关于带注释的C#4规范的第290和291页的评论,以深入讨论此特定规则。

而且,治所在今相交的Color Color规则一个有趣的极端情况的分析,见

http://blogs.msdn.com/b/ericlippert/archive/2009/07/06/color-color.aspx

+0

我们在哪里可以找到这个讨论?我没有在MSDN上看到允许评论的规范版本。 – 2013-02-21 23:31:50

+0

@EricLippert:它在右侧吧:)。 – Dhananjay 2013-02-22 15:02:24

+1

@EdT:转到ericlippert.com。点击右侧边栏上的“The C#Programming Language”链接。购买这本书;注释是值得的价格。等到它到来。转到第290页。 – 2013-02-22 15:09:34

相关问题