C# 编译器是否识别继承的属性类以检查它们是否包含“属性”一词?在下面的示例中,[Blah]、[BlahA] 和 [BlahAttribute] 是可用属性
[AttributeUsage(AttributeTargets.Method, AllowMultiple=true)]
public class BlahA: Attribute
{
//stuff
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple=true)]
public class BlahAttribute: Attribute
{
//stuff
}
//acceptable
[BlahA]
[Blah]
[BlahAttribute]
Run Code Online (Sandbox Code Playgroud)
规范对此相当清楚:
从抽象类 System.Attribute 派生的类,无论是直接还是间接,都是属性类。属性类的声明定义了一种可以放置在声明上的新属性。按照惯例,属性类以 Attribute 后缀命名。属性的使用可以包含或省略此后缀。
在属性规范下进一步向下:
按照惯例,属性类的命名后缀为
Attribute. 形式为type_name的attribute_name可以包含或省略此后缀。如果发现有和没有此后缀的属性类,则存在歧义,并导致编译时错误。如果attribute_name的拼写使得其最右侧的标识符是逐字标识符 (...),则仅匹配没有后缀的属性,从而能够解决这种歧义。
(来源)
由于规范允许用户包含或省略此后缀,因此 C# 编译器也允许两者。在你的情况下,[BlahA]指的是BlahA,而[Blah]并[BlahAttribute]指BlahAttribute。如果你定义的名称属性Blah,[Blah]将是不明确的,你必须通过指定解决它[BlahAttribute]或使用逐字标识符[@Blah]。
更新:
我潜伏在 Roslyn 的源代码中,完全是偶然发现了负责规范这一部分的部分。您可以在Microsoft.CodeAnalysis.CSharp.Binder.Binder_Lookup(至少截至 2020 年 5 月 19 日)中找到它们。碰巧在那里描述了一个相当有趣的边缘情况。当解析属性的类型时,它们会查找Foo并FooAttribute如上所述。如果其中只有一个是可行的,则他们选择那个,如果两者都是,则报告歧义。但是,如果其中一个是可行的,而另一个本身不明确,他们就会慷慨地选择单一可行的选项。为了说明这一点,请考虑:
namespace A
{
class FooAttribute : System.Attribute
{
}
}
namespace B
{
class FooAttribute : System.Attribute
{
}
}
namespace C
{
class Foo : System.Attribute
{
}
}
namespace D
{
using A;
using B;
using C;
[Foo] // Attribute resolution.
class Bar
{
}
}
Run Code Online (Sandbox Code Playgroud)
属性解析的候选对象是A.FooAttribute和B.FooAttribute使用Attribute后缀C.Foo查看时以及不使用后缀查看时。第一个选择是不明确的 - 编译器无法在A's 和B's 版本之间做出决定- 所以C.Foo选择了上面的代码并编译。现在删除using Aorusing B指令中的任何一个。突然间,只有一个FooAttribute是可行的候选者,并且它和 之间存在歧义C.Foo。试试吧!