Applying Attributes

In .NET code is compiled to an assembly. The assembly contains IL code and metadata. IL is a state between source code and machine language. By emitting IL the CLR has one more opportunity to examine code before it is just-in-time compiled (JITted) and to compare the security permissions requested against those granted. Also, the total number of compilers and JITters is dramatically reduced (see the note). In addition to IL, .NET assemblies contain metadata. Metadata allows assemblies to carry additional information around that uniquely identifies the assembly. Metadata is added to assemblies via attributes.

NOTE

A second reason to compile code to a byte code form (IL is analogous to Java byte code) is to reduce the number of compilers that must be written. If a compiler writes to a machine language, vendors will need to produce one compiler for every machine operating system and every language supported. By compiling to an intermediate form, vendors need to write only one compiler per language and one JITter per operating system.

The reasoning is pretty straightforward, as presented by John Gough [2002] in Compiling for the .NET Common Language Runtime (CLR) . If all byte code is created equal, there needs to be only one JITter per operating system that converts any byte code compiled from any language into machine language suitable for the target operating system. Instead of one compiler for each language for each operating system, compiling to an intermediate form results in one compiler per language and one JITter per operating system.

Suppose there were 10 languages supported on 10 operating systems. Without an intermediate form there would be 10 x 10 compilers. With an intermediate form there are only 10 compilers and 10 JITters, or 20 applications instead of 100.

The concept of metadata was implemented for .NET to help relieve "DLL hell." The role of attributes is to associate metadata, or extra information, with an assembly. Metadata mitigates the need to store information that uniquely identifies an application in a separate location, the registry. Attributes play a central role here.

Reviewing Attribute Conventions

Like everything else in .NET, attributes are just classes. The good news is that if you know how to use classes, you have won half the battle. (We'll cover conventions for custom attributes in the Creating Custom Attributes section later in the chapter.)

NOTE

Any class can be used as an attribute, but it will probably lead to a lot of confusion if you do so. However, it might be fun to experiment with classes applied as attributes even if your adventures aren't especially fruitful.

By convention attributes are classes with an Attribute suffix. When you apply an attribute, by convention you drop the Attribute suffix. For example, if you apply the DefaultMemberAttribute to a class, you drop the Attribute suffix and apply the attribute using attribute notation, as demonstrated below.

 <DefaultMember("Item")> Public Class HasDefaultMember ... 

NOTE

I'm not sure why dropping the Attribute suffix is supported or has become a convention. I can't think of a logical explanation, so I guess that dropping the Attribute suffix is supported for convenience.

The attribute is applied in the same line as the entity it is applied to in Visual Basic .NET. If you want to apply the attribute in the preceding line, you must use the line continuation character ( _ ). Here is the DefaultMemberAttribute applied to HasDefaultMember using the line continuation character.

 <DefaultMember("Item")> _ Public Class HasDefaultMember ... 

It is important to recognize that you are invoking a constructor call with the attribute syntax. The parameters you are passing, called positional arguments or named arguments (not shown in the code fragment above), are parameters to the Attribute class constructor. Refer to the Creating Custom Attributes section for more information on positional and named arguments.

Applying Attributes to Entities

The most common use of attributes is to apply them to assemblies, classes, other types, or members . Since there are a lot of attributes (and we have the ability to create additional custom attributes), I won't make any effort to exhaustively list available attributes. Instead I will demonstrate several examples that show how to apply attributes and introduce a few interesting attributes, so you will know how to apply any attribute you encounter.

Using the DefaultMemberAttribute , DefaultPropertyAttribute , and Default Modifier

The DefaultMemberAttribute is applied to indicate that a member of a class is the default member that will be called when the InvokeMember method is invoked. For example, if you obtain a Type object for a specific type and use that object to call InvokeMember , you can leave blank the member name argument for InvokeMember . If a default member exists, it will be called by InvokeMember . Listing 5.1 offers an example.

NOTE

These three subjects were placed in this section together because they sound related . Doing so provided me with an opportunity to introduce two new attributes and clarify the distinction between DefaultPropertyAttribute and the Default modifier.

Listing 5.1 Implementing and Invoking Default Members Using Reflection
 <DefaultMember("IsDefault")> _ Public Class HasDefaultMember   Public Shared Sub IsDefault()     Console.WriteLine("IsDefault DefaultMember invoked")   End Sub End Class Public Sub TestDefaultMember()   Dim t As Type = _     Type.GetType("DefaultMemberDemo.HasDefaultMember")   ' We don't need the default member's name   t.InvokeMember("", BindingFlags.Static Or _     BindingFlags.Public Or BindingFlags.InvokeMethod, _     Nothing, Nothing, Nothing) End Sub 

HasDefaultMember in Listing 5.1 demonstrates how to apply the DefaultMemberAttribute to the class, supplying the name of the member that is the default member. The second method ” TestDefaultMember ”demonstrates how to invoke the default member using Reflection. (Chapter 4 covered Reflection in depth.) Notice that we did not need to express the member name ”the first argument to InvokeMember ”to invoke the default member. The DefaultPropertyAttribute is applied at the class level too. (Technically it is limited to the class level because of the AttributeUsageAttribute used when the DefaultPropertyAttribute was defined. Refer to the Creating Custom Attributes section later in this chapter for more information.) The DefaultPropertyAttribute is defined in the System.ComponentModel namespace and is initialized with the name of the component property that will be the default selected property in the Properties window when the control is selected. The DefaultPropertyAttribute is applied identically to the DefaultMemberAttribute . (Refer to Chapter 9 for more information.)

If you are trying to make a property behave as the default property, for example, when you use an object in a context where a property is expected, you want to use the Default modifier. The Default modifier indicates that a property is the default property; however, this modifier can be applied only to indexed properties in .NET. The reason for this is that VB6 used Set , Get , and Let , which acted as cues to the compiler that you were referring to a property when no property was typed in the code. VB .NET does not support Get , Set , and Let for properties, but a cue is still needed. Thus, the array operators play the role of cues to the compiler. Listing 5.2 demonstrates how to define a class that uses the Default modifier and a client method that interacts with the default property.

Listing 5.2 Using the Default Property Modifier on Indexed Properties
 Public Class HasDefault   Inherits ReadOnlyCollectionBase   Public Shared Function CreateNew() As HasDefault     Dim Instance As HasDefault = New HasDefault()     Instance.InnerList.Add("Never ")     Instance.InnerList.Add("mistake ")     Instance.InnerList.Add("a ")     Instance.InnerList.Add("clear ")     Instance.InnerList.Add("view ")     Instance.InnerList.Add("for ")     Instance.InnerList.Add("a ")     Instance.InnerList.Add("short ")     Instance.InnerList.Add("distance")     Instance.InnerList.Add("!")     Return Instance   End Function   Default Public ReadOnly Property Item(ByVal Index As Integer) As String   Get     Return CType(InnerList(Index), String)   End Get   End Property End Class Public Sub TestDefault()   Dim HasDefault As HasDefault = HasDefault.CreateNew   Dim I As Integer   For I = 0 To HasDefault.Count - 1     Console.Write(HasDefault(I))   Next   Console.WriteLine() End Sub 

The Default modifier is employed in the ReadOnly property Item defined in the HasDefault class that inherits from ReadOnlyCollectionBase . ( ReadOnlyCollectionBase is defined in System.Collections ; hence, you will have to use an Imports statement with the code as it is.) The TestDefault subroutine creates an instance of the HasDefault class (by using the factory method CreateNew ) and uses a For loop to iterate over all the elements in the HasDefault object. Notice that I am not explicitly referring to the Item property in the line of code Console.Write(HasDefault(I)) ; however, if you place a breakpoint in the Item getter method, you will see that the Default property is being invoked. This is true even though the code looks as if we are referring to an array of HasDefault objects.

Using the Obsolete Member Attribute

The ObsoleteAttribute is used to mark elements of code that are being deprecated in future versions and will no longer be supported. The basic version of the ObsoleteAttribute requires no parameters and will display a message in the Task List indicating that the element is obsolete (see Figure 5.1). Listing 5.3 demonstrates an example of the ObsoleteAttribute .

Listing 5.3 Using the ObsoleteAttribute
 <Obsolete()> _ Public Sub TestDefault()   Dim HasDefault As HasDefault = HasDefault.CreateNew   Dim I As Integer   For I = 0 To HasDefault.Count - 1     Console.Write(HasDefault(I))   Next   Console.WriteLine() End Sub 
Figure 5.1. A message in the Task List indicating elements marked as obsolete.

graphics/05fig01.gif

You can pass a custom string message and a Boolean indicating whether the obsolete element is treated as an error by the compiler. The ObsoleteAttribute can be applied to all elements of code except parameters, return values, modules, and assemblies.

There are a large number of existing attributes, and because attributes are just classes you can expect that number to grow. The most important thing is to realize how an attribute is applied and then to look for attributes just as you would look for preexisting code when you have a particular problem to solve. To recap, when you apply an attribute, you are essentially constructing an instance of that attribute's class, passing the positional and named arguments to initialize the class.

NOTE

If you want to find some specific examples of code, you can download the shared Common Language Infrastructure dubbed Rotor . By using a tool like grep you can search the source code in Rotor for a specific example of code, such as classes that define attributes applicable to parameters. Here is an example of such a command: grep “i “d "AttributeTargets.Parameter" *.cs . A compilable version of grep actually ships with the Rotor source code.

How attributes are used is dictated by the AttributeUsageAttribute and the AttributeTargets enumeration. Refer to the Specifying Attribute Usage section later in this chapter for more information.



Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net