You use attributes to connect metadata, including compiler directives, to an element in your code. There are plenty of attributes built into C#, and we've already seen a number of them in this book. For example, this attribute lets you indicate that a method is a Web method, accessible from a Web service: [WebMethod] This attribute lets you indicate that a class can be serialized: [Serializable] This attribute indicates that you want to import a DLL; in this case, kernel32.dll: [DllImport("kernel32", SetLastError=true)] Note the syntax in this case. We're passing text to the DLLImport attribute, followed by the expression SetLastError=true . We also saw that you can use attributes to specify metadata about specific elements, as here, where we're setting the version and title of an assembly: [assembly:AssemblyVersion Attribute("1.0.0.0")] [assembly:AssemblyTitleAttribute("Example ch14_01")]
Also note that the attributes here are prefixed with the text assembly: , which is an attribute target . Specifying Attribute TargetsSometimes, it might not be clear what the target of an attribute is. For example, if you use an attribute just before a method, does it apply to the method or to the assembly as a whole? To make sure that the target of an attribute is clear to the compiler, you can specify one of a set of predefined targets like this: [ target : attribute-list ] Here are the parts of this attribute:
You can see the predefined targets for attributes, as defined in the AttributeTargets enumeration, in Table 14.1. Table 14.1. Predefined Attribute Targets
Conditional AttributesAttributes can contain directives to the compiler, and one consequence of that is that besides standard attributes, you can also use conditional attributes . Conditional attributes are much like the preprocessor directives we saw in Chapter 1, "Essential C#," although they're designed exclusively for use in method declarations. These attributes determine whether a symbol has been defined, and if so, include the following code at compile time. Here's how you use them in general: [Conditional( conditionalSymbol )] In this case, conditionalSymbol is a symbol you can define with the #define preprocessor directive. Here's an example, the Debugger class, where we'll include code for use only when we're debugging. If you define a symbol you name DEBUGGING in this code (using the #define preprocessor directive), the method following the [Conditional] attribute, which displays a debugging message, is included at compile time: #define DEBUGGING using System; using System.Diagnostics; public class Debugger { [Conditional("DEBUGGING")] public static void Write(string text) { System.Console.WriteLine(text); } } Now you can call Debugger.Write in other code; if DEBUGGING is defined, that method will display the text passed to it. If the #define DEBUGGING line is commented out, calling Debugger.Write won't display any text. You can see this at work in ch14_01.cs, Listing 14.1. Listing 14.1 Creating a Custom Conditional Attribute (ch14_01.cs)#define DEBUGGING using System; using System.Diagnostics; public class Debugger { [Conditional("DEBUGGING")] public static void Write(string text) { System.Console.WriteLine(text); } } class ch14_01 { static void Method1() { Debugger.Write("Now in Method1."); Method2(); } static void Method2() { Debugger.Write("Now in Method2."); } public static void Main() { Method1(); System.Console.WriteLine("Did it work?"); } } Here's what you see when you run ch14_01.cs as it appears in Listing 14.1: C:\>ch14_01 Now in Method1. Now in Method2. Did it work? If you comment out the #define DEBUGGING line in the code, you'll see this instead: C:\>ch14_01 Did it work? |