Curious ambiguity in attribute specification (two using directives)

What we have here are two concepts conflated.


1. How the compiler knows what class implements an attribute

There is a simple convention in place that attributes may be referred to by either the class name or the class name less an attribute suffix. So when you add the [Horse] annotation to someIdentifier like this,

[Horse]
someIdentifier

the implementation of [Horse] must be a class that inherits Attribute that is called either HorseAttribute or Horse.

Note: There is a widely accepted convention that all classes that implement attributes should have "Attribute" suffixed to the type name.

2. How the compiler know's which type code is referring to

When we refer to a type, in code, the compiler looks for a definition of that type that has been loaded into the namespace. If there are multiple definitions for that type in the namespace the compiler does nothing to resolve that ambiguity, it is up to the developer to improve the code. The compiler can't choose so raises error CS1040.

The compiler does not do any semantic or static analysis to divine the coders intent. It would be hard to define, costly to perform and prone to error.

This error is not thrown solely when finding implementations for attributes.


In your compiling examples there is no ambiguity around point 2, so the code compiles.

If the resolution of point 1 leads to a type name that is ambiguous be it, Horse or HorseAttribute, then errors will come from point 2.

The compiler does not make special allowances, e.g I'm performing point 2 in response to point 1 so, if I have an ambiguity in this case is there a special fallback position for point 2s performed for point 1s?

If you consider the level of additional complexity and time that special provisions introduce you may accept that it would be better to require a level of stringency from code authors.

In my opinion and, that of others, requiring code that avoids this kind of ambiguity leads to code that is easier to understand by others and one's future self. This makes the discussion of why somewhat moot, as we could argue effort applied here by the compiler team would have enabled "smellier", harder to maintain code.


NOTE: Further to the answer

When you consider behaviour exhibited by the example from the Langauge specification

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 {}

Try here

I would agree there is a confusion and indeed, an inconsistency in the way the compiler treats definitions from one namespace and those imported from different namespaces.