首页 > 解决方案 > 属性规范中奇怪的歧义(两个 using 指令)

问题描述

背景:属性规范中,有时有两种有效的方式来编写应用的属性。例如,如果一个属性类的名称为HorseAttribute,您可以将该属性应用为 any[HorseAttribute]或 just [Horse]。歧义可以用 a 来解决@,例如[@Horse].

以下是一个有效的程序:

using System;
using Alpha;
using Beta;

namespace N
{
  [Horse]
  class C
  {
  }
}

namespace Alpha
{
  // valid non-abstract attribute type with accessible constructor
  class HorseAttribute : Attribute
  {
  }
}

namespace Beta
{
  // any non-attribute type with that name
  enum Horse
  {
  }
}

Alpha.HorseAttribute当我只写[Horse]. 毕竟,该类型Beta.Horse完全不适合在属性规范中使用。

即使我交换名称,C# 编译器也会知道该怎么做:

using System;
using Alpha;
using Beta;

namespace N
{
  [Horse]
  class C
  {
  }
}

namespace Alpha
{
  // valid non-abstract attribute type with accessible constructor
  class Horse : Attribute
  {
  }
}

namespace Beta
{
  // any non-attribute type with that name
  enum HorseAttribute
  {
  }
}

同样,编译器知道我想要Alpha.Horse.


现在是我想问的代码。它与上述相同,只是现在两种类型具有相同的名称:

using System;
using Alpha;
using Beta;

namespace N
{
  [Horse]
  class C
  {
  }
}

namespace Alpha
{
  // valid non-abstract attribute type with accessible constructor
  class Horse : Attribute
  {
  }
}

namespace Beta
{
  // any non-attribute type with that name
  enum Horse
  {
  }
}

现在,C# 编译器拒绝构建,说:

错误 CS0104:“Horse”是“Alpha.Horse”和“Beta.Horse”之间的模糊引用

在线尝试!

我的问题是,为什么编译器不能在这种情况下选择正确的,而它在前面的两个示例中做得很好?

这种行为是否符合 C# 语言规范?实际上是否需要 C# 编译器在这里发出错误?

(当然我知道我可以通过[Alpha.Horse]明确地说来解决它,所以我不是在要求那个“解决方案”。)

标签: c#compiler-errorscustom-attributes.net-attributes

解决方案


我们这里有两个概念混为一谈。


1. 编译器如何知道哪个类实现了一个属性

有一个简单的约定,属性可以由类名或类名减去属性后缀来引用。所以当你像这样添加[Horse]注释时,someIdentifier

[Horse]
someIdentifier

的实现[Horse]必须是一个继承的类,Attribute称为HorseAttributeor Horse

注意:有一个被广泛接受的约定,即所有实现属性的类都应该在类型名称后面加上“Attribute”。

2. 编译器如何知道代码所指的类型

当我们在代码中引用类型时,编译器会查找已加载到命名空间中的该类型的定义。如果命名空间中有该类型的多个定义,则编译器不会采取任何措施来解决这种歧义,则由开发人员来改进代码。编译器无法选择,因此会引发错误 CS1040

编译器不做任何语义或静态分析来判断编码器的意图。这将难以定义、执行成本高且容易出错。

此错误不仅仅在查找属性的实现时引发。


在您的编译示例中,第 2 点没有歧义,因此代码可以编译。

如果第 1 点的解析导致类型名称不明确,Horse或者HorseAttribute,则错误将来自第 2 点。

编译器没有特别考虑,例如我正在执行第 2 点以响应第 1 点,所以,如果我在这种情况下有歧义,是否有针对第 1 点执行的第 2 点的特殊回退位置?

如果您考虑特殊规定引入的额外复杂性和时间水平,您可能会接受要求代码作者严格程度会更好。

在我和其他人看来,要求避免这种歧义的代码会导致代码更容易被他人和未来的自己理解。这使得讨论为什么有些没有实际意义,因为我们可以争辩说编译器团队在这里所付出的努力会使“更臭”,更难维护代码。


注意:进一步回答

当您考虑语言规范中的示例所展示的行为时

using System;

[AttributeUsage(AttributeTargets.All)]
public class X: Attribute
{}

[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}

[X]                     // Error: ambiguity
class Class1 {}

[XAttribute]            // Refers to XAttribute
class Class2 {}

[@X]                    // Refers to X
class Class3 {}

[@XAttribute]           // Refers to XAttribute
class Class4 {}

在这里试试

我同意存在混淆,实际上,编译器处理来自一个命名空间的定义和从不同命名空间导入的定义的方式不一致。


推荐阅读