Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Using Custom Attributes

Attributes, such as public, private, static, and so on, can be applied to types and members. I think we’d all agree on the usefulness of applying attributes, but wouldn’t it be even more useful if we could define our own attributes? For example, what if I could define a type and somehow indicate that the type can be remoted via serialization? Or maybe I could apply an attribute to a method to indicate that certain security permissions must be granted before the method can execute.

Of course, creating and applying user-defined attributes to types and methods would be great and convenient, but it would require the compiler to be aware of these attributes so it would emit the attribute information into the resulting metadata. Because compiler vendors usually prefer not to release the source code for their compiler, Microsoft came up with another way to allow user-defined

attributes. This mechanism, called custom attributes, is an incredibly powerful mechanism that’s useful at both application design time and run time. Anyone can define and use custom attributes, and all compilers that target the common language runtime (CLR) must be designed to recognize custom attributes and emit them into the resulting metadata.


The first thing you should realize about custom attributes is that they’re just a way to associate additional information with a target. The compiler emits this additional information into the managed module’s metadata. Most attributes have no meaning for the compiler; the compiler simply detects the attributes in the source code and emits the corresponding metadata.

The .NET Framework Class Library (FCL) defines literally hundreds of custom attributes that can be

applied to items in your own source code. Here are some examples:

 

■ Applying the DllImport attribute to a method informs the CLR that the implementation of

the method is actually in unmanaged code contained in the specified DLL.

 

■ Applying the Serializable attribute to a type informs the serialization formatters that an

instance’s fields may be serialized and deserialized.

 

■ Applying the AssemblyVersion attribute to an assembly sets the version number of the as- sembly.

■ Applying the Flags attribute to an enumerated type causes the enumerated type to act as a

set of bit flags.

 

Following is some C# code with many attributes applied to it. In C#, you apply a custom attribute to a target by placing the attribute in square brackets immediately before the target. It’s not impor- tant to understand what this code does. I just want you to see what attributes look like.

 

using System;

using System.Runtime.InteropServices;

 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class OSVERSIONINFO {

public OSVERSIONINFO() {

OSVersionInfoSize = (UInt32) Marshal.SizeOf(this);

}

 

public UInt32 OSVersionInfoSize = 0; public UInt32 MajorVersion = 0; public UInt32 MinorVersion = 0; public UInt32 BuildNumber = 0; public UInt32 PlatformId = 0;



 

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public String CSDVersion = null;

}

 

internal sealed class MyClass {

[DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)] public static extern Boolean GetVersionEx([In, Out] OSVERSIONINFO ver);

}


In this case, the StructLayout attribute is applied to the OSVERSIONINFO class, the MarshalAs attribute is applied to the CSDVersion field, the DllImport attribute is applied to the GetVersion­ Ex method, and the In and Out attributes are applied to GetVersionEx’s ver parameter. Every programming language defines the syntax a developer must use in order to apply a custom attribute to a target. Microsoft Visual Basic .NET, for example, requires angle brackets (<, >) instead of square brackets.

The CLR allows attributes to be applied to just about anything that can be represented in a file’s metadata. Most commonly, attributes are applied to entries in the following definition tables: TypeDef (classes, structures, enumerations, interfaces, and delegates), MethodDef (including constructors), ParamDef, FieldDef, PropertyDef, EventDef, AssemblyDef, and ModuleDef. Specifically, C# allows you to apply an attribute only to source code that defines any of the following targets: assembly, module, type (class, struct, enum, interface, delegate), field, method (including constructors), method param- eter, method return value, property, event, and generic type parameter.

When you’re applying an attribute, C# allows you to specify a prefix specifically indicating the target the attribute applies to. The following code shows all of the possible prefixes. In many cases, if you leave out the prefix, the compiler can still determine the target an attribute applies to, as shown in the previous example. In some cases, the prefix must be specified to make your intentions clear to the compiler. The prefixes shown in italics in the following code are mandatory.

 

using System;

 

[assembly: SomeAttr] // Applied to assembly

[module: SomeAttr] // Applied to module

 

[type: SomeAttr] // Applied to type

internal sealed class SomeType<[typevar: SomeAttr] T> { // Applied to generic type variable

 

[field: SomeAttr] // Applied to field public Int32 SomeField = 0;

 

[return: SomeAttr] // Applied to return value [method: SomeAttr] // Applied to method public Int32 SomeMethod(

[param: SomeAttr] // Applied to parameter Int32 SomeParam) { return SomeParam; }

 

[property: SomeAttr] // Applied to property public String SomeProp {

[method: SomeAttr] // Applied to get accessor method get { return null; }

}

 

[event: SomeAttr] // Applied to event

[field: SomeAttr] // Applied to compiler­generated field

[method: SomeAttr] // Applied to compiler­generated add & remove methods public event EventHandler SomeEvent;

}


Now that you know how to apply a custom attribute, let’s find out what an attribute really is. A custom attribute is simply an instance of a type. For Common Language Specification (CLS) com- pliance, custom attribute classes must be derived, directly or indirectly, from the public abstract System.Attribute class. C# allows only CLS-compliant attributes. By examining the .NET Frame- work SDK documentation, you’ll see that the following classes (from the earlier example) are defined: StructLayoutAttribute, MarshalAsAttribute, DllImportAttribute, InAttribute, and OutAttribute. All of these classes happen to be defined in the System.Runtime.InteropSer­ vices namespace, but attribute classes can be defined in any namespace. Upon further examina- tion, you’ll notice that all of these classes are derived from System.Attribute, as all CLS-compliant attribute classes must be.

       
   
 
 

 

As I mentioned earlier, an attribute is an instance of a class. The class must have a public construc- tor so that instances of it can be created. So when you apply an attribute to a target, the syntax is similar to that for calling one of the class’s instance constructors. In addition, a language might permit some special syntax to allow you to set any public fields or properties associated with the attribute class. Let’s look at an example. Recall the application of the DllImport attribute as it was applied to the GetVersionEx method earlier.

 

[DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)]

 

The syntax of this line should look pretty strange to you because you could never use syntax like this when calling a constructor. If you examine the DllImportAttribute class in the documentation, you’ll see that its constructor requires a single String parameter. In this example, "Kernel32" is being passed for this parameter. A constructor’s parameters are called positional parameters and are mandatory; the parameter must be specified when the attribute is applied.

What are the other two “parameters”? This special syntax allows you to set any public fields or properties of the DllImportAttribute object after the object is constructed. In this example, when the DllImportAttribute object is constructed and "Kernel32" is passed to the constructor, the object’s public instance fields, CharSet and SetLastError, are set to CharSet.Auto and true, respectively. The “parameters” that set fields or properties are called named parameters and are optional because the parameters don’t have to be specified when you’re applying an instance of the attribute. A little later on, I’ll explain what causes an instance of the DllImportAttribute class to actually be constructed.

Also note that it’s possible to apply multiple attributes to a single target. For example, in this chapter’s first program listing, the GetVersionEx method’s ver parameter has both the In and Out attributes applied to it. When applying multiple attributes to a single target, be aware that the order


of attributes has no significance. Also, in C#, each attribute can be enclosed in square brackets, or multiple attributes can be comma-separated within a single set of square brackets. If the attribute class’s constructor takes no parameters, the parentheses are optional. Finally, as mentioned earlier, the Attribute suffix is also optional. The following lines behave identically and demonstrate all of the possible ways of applying multiple attributes.

 

[Serializable][Flags] [Serializable, Flags]

[FlagsAttribute, SerializableAttribute] [FlagsAttribute()][Serializable()]

 


Date: 2016-03-03; view: 772


<== previous page | next page ==>
Nbsp;   Delegates and Reflection | Nbsp;   Defining Your Own Attribute Class
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)