Classes in .NET contain all kinds of declarative information that describes how the class works. Modifiers like Public, Private, and Friend describe the accessibility of a class, field, or method. Attributes like MustInherit, NotInheritable, and Overrides describe the inheritance- and polymorphic-related behaviors that a class exhibits. Also, data can be declared as a specific type.
Throughout this chapter, reflection has been used to query types, load them, and make method calls on them. Virtually any secret can be discovered by reflection. This ability by itself is pretty amazing when you think of the kinds of architectures that can be accommodated with it. However, there is another level beyond this: declarative attributes can be created programmatically and associated with any entity in .NET. These attributes are stored with the type's metadata and are available at all times: design time, compile time, and runtime. Reflection can examine this information and make decisions based on it.
Quite a few attributes are already defined in the framework. For instance, both the Visual Basic .NET and C# compilers recognize the Obsolete attribute. The attribute allows the programmer to associate a warning with a class.
In Visual Basic .NET, the System.ObsoleteAttribute attribute (like all attributes) is contained within angular brackets on the same line as the class definition. Unfortunately, this containment is unwieldy:
<Obsolete("Try NewClass instead.")> Public ...