Attribute Identifiers
Look at the following line of code, and try to figure out whether the attribute annotates the return value or the method: -
class MyClass { [HRESULT] public long Foo(); }
If you have some COM experience, you know that an HRESULT is the standard return type for all methods not named AddRef or Release. However, it's easy to see that if the attribute name could apply to both the return value and the method name, it would be impossible for the compiler to know what your intention is. Here are some other scenarios where the compiler would not know your intention by context: -
- method vs. return type
- event vs. field vs. property
- delegate vs. return type
- property vs. accessor vs. return value of getter method vs. value parameter of setter method
In each of these cases, the compiler makes a determination based on what's considered "most common." To override this determination, you use an attribute identifier, all of which are listed here: -
- assembly
- module
- type
- method
- property
- event
- field
- param
- return
To use an attribute identifier, preface the attribute name with the identifier and a colon. In the MyClass example, if you wanted to make sure the compiler could determine that the HRESULT attribute is meant to annotate the return value and not the method, you would specify it as follows: -
class MyClass { [return:HRESULT] public long Foo(); }
Summary
C# attributes provide a mechanism for annotating types and members at design time with information that can later be retrieved at run time through reflection. This gives you the ability to create truly self-contained, self-describing components without having to resort to stuffing necessary bits into resource files and constants. The advantage is a more mobile component that is both easier to write and easier to maintain.