属性指定中的歧义疑问:同名类型下编译器为何无法正确解析?
这个问题得从C#编译器处理属性名称的两步走逻辑说起,搞清楚这两步的顺序,你就能明白为啥前两个例子没问题,第三个就炸了。
先唠唠前两个例子为啥能正常工作
第一个例子:属性类带Attribute后缀
当你写[Horse]的时候,编译器会自动做两件事:
- 先找有没有直接叫
Horse的类型 - 再找有没有叫
HorseAttribute的类型(这是C#给属性加的语法糖,省得每次都写全后缀)
在第一个例子里:
- Beta里的
Horse是枚举,就算找到了也不符合属性的要求(得是继承自Attribute的非抽象类才行) - Alpha里的
HorseAttribute刚好通过后缀匹配找到,而且完全符合属性的所有条件
所以编译器直接选了Alpha的那个,把Beta的枚举直接忽略了。
第二个例子:属性类不带Attribute后缀
这里Alpha的Horse本身就继承了Attribute,是合法的属性类型;Beta的是HorseAttribute(枚举)。写[Horse]的时候:
- 编译器直接找
Horse,只有Alpha的那个匹配上,而且是合法属性 - 后缀匹配的
HorseAttribute是Beta的枚举,不符合要求,但这不影响——毕竟已经找到合适的直接匹配项了
所以编译器顺利选中Alpha的Horse,没毛病。
第三个例子为啥就报歧义了?
这里的关键问题是:两个命名空间里都有个叫Horse的类型——一个是合法属性(Alpha的),一个是枚举(Beta的)。
编译器处理代码的时候,是先做名称查找,再做属性适用性检查的。也就是说,它得先搞清楚你写的Horse到底指的是哪个类型,才能去判断这个类型能不能当属性用。
现在Horse这个名字同时对应两个不同的类型,编译器在名称查找这一步就懵了:你到底说的是Alpha的那个还是Beta的?这时候它不会管后面哪个适合当属性,直接就抛出歧义错误(CS0104)——因为连你指的是哪个类型都确定不了,谈后续的检查根本没意义。
这符合C#规范吗?编译器必须报错吗?
完全符合规范,而且编译器必须报错。
C#语言规范明确规定:当名称查找结果出现多个可访问的同名类型,又没有额外的上下文(比如显式写Alpha.Horse)来消除歧义时,编译器必须抛出歧义引用错误。
说白了,编译器不会“自作聪明”地跳过名称歧义去判断哪个类型更适合当属性——名称解析是最基础的语法分析步骤,必须先明确你引用的对象,才能进行后续的语义验证。
内容的提问来源于stack exchange,提问作者Jeppe Stig Nielsen

