假设:
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);
}
}
错误:
无法使用实例访问成员'Derived.M(object)' 参考;使用类型名称来限定它
查看C#4.0规范部分7.4(成员查找),第一个要点是:
在类型T中使用K类型参数的名称N的成员查找是 处理如下:
[...]包含覆盖修饰符的成员将被排除在外 设置[名为N的可访问成员]
由此我得出结论,无法再访问覆盖Derived.M
。相反,编译器必须引用BaseClass.M
。
但是,这并不能解释为什么添加静态Derived.M
会突然导致编译错误。编译器现在只能看到静态成员Derived.M
,并断定此成员是无效调用。如果我删除静态Derived.M
,那么编译成功。
为什么会这样?
答案 0 :(得分:7)
以下步骤似乎发生并且是编译器错误的原因:
M
的实例版本,只留下编译器的静态版本。静态参数类型为object
,与int
兼容。方法名称也匹配。 我无法引用证明这一点的单一句子,但是§7.4(成员查找)和§3.5(可访问性)都没有谈到static
与实例的对比,所以我认为这个事实很简单在进行成员查找时,没有考虑 。
§7.4中的相关部分似乎是:
在类型T中使用K类型参数的名称N的成员查找按如下方式处理:
- 首先,确定一组名为N的可访问成员:
[...]
该集由T中名为N的所有可访问(§3.5)成员组成,包括继承成员和对象中名为N的可访问成员。如果T是构造类型,则通过替换类型参数来获得成员集,如第10.3.2节中所述。包含覆盖修饰符的成员将从集合中排除。
我理解这部分的方式如上所述:它将返回实例方法和静态方法,然后删除实例1,因为它具有override
修饰符。
此时只剩下静态方法。
它以此结束:
最后,[...]确定查找结果:
如果集合只包含方法,那么这组方法就是查找的结果。
因此,结果是静态方法。
显然,这个问题只发生在你有一个类层次结构的情况下,其中一个派生类声明了一个具有相同名称和兼容参数的静态方法。
将这样的静态方法添加到现有类是一种简单地向类添加内容仍然是重大更改的情况。
虽然我很确定你知道如何解决编译器错误,但我仍然会说出来,要有一个完整的答案:
使用任何基类作为变量的编译时类型。运行时类型仍然可以是派生类型,这不是问题:
BaseClass d = new Derived();
// ^ ^
// compile time type runtime type
答案 1 :(得分:4)
丹尼尔的回答是正确的;相关规则的简要摘要是:
这最后一条规则有点奇怪,但确实有道理。有关此特定规则的扩展讨论,请参阅我的注释和nikov对精选注释C#4规范的第290和291页的评论。
此外,对于此规则与Color Color
规则相交的有趣角点情况的分析,请参阅
http://blogs.msdn.com/b/ericlippert/archive/2009/07/06/color-color.aspx