Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 77 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
77
Dung lượng
852,79 KB
Nội dung
A Day for Reflection and Attributes 703 21 These values in this enumeration are the flags listed in Table 21.3. To include more than one attribute restriction from the table, you use the | operator. The following shows how to use the AttributeUsage attribute to restrict a new attribute to structures and classes: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] Defining the Attribute Class You define an attribute similarly to defining a regular class. After all, an attribute is really just another class—an attribute class. You’ve already seen the class header for declaring an attribute. In addition to the header, you need to set up any parameters and the ele- ments within the body. There are restrictions on the parameters for an attribute class. You can use only simple types, such as bool, byte, char, short, int, long, float, and double. Additionally, you can use string, System.Type, and enum. A parameter can also be defined as a one-dimensional array, as long as the array type is one of the standard types already mentioned. Finally, a parameter can be of type object. If it is declared of type object, when a value is passed to an instantiated object of the attribute class, it must also be of the types already men- tioned. Listing 21.3 presents a code snippet for a custom attribute that can be used to track the status of a code listing, who the coder is, and who the tester is. Listings 21.3–21.5 are not complete, so you will not be able to successfully compile and execute them. Listing 21.6 pulls together these snippets into a complete solution. Caution LISTING 21.3 CodeStatus.cs—A Custom Attribute Class 1: using System; 2: 3: [AttributeUsage(AttributeTargets.All)] 4: public class CodeStatusAttribute : System.Attribute 5: { 6: private string pSTATUS; 7: private string pTESTER; 8: private string pCODER; 9: 10: public CodeStatusAttribute( string Status ) 11: { 12: this.pSTATUS = Status; 13: } 14: 15: public string Tester 16: { 17: set 18: { 19: pTESTER = value; 20: } 21: get 22: { 23: return pTESTER; 24: } 25: } 26: 26: public string Coder 27: { 28: set 29: { 30: pCODER = value; 31: } 32: get 33: { 34: return pCODER; 35: } 36: } 37: 38: public override string ToString() 39: { 40: return pSTATUS; 41: } 42: } 704 Day 21 LISTING 21.3 continued C# enables you to use an attribute named xxxAttribute by simply typing [xxx()]. Note Listing 21.3 creates a custom attribute class named CodeStatusAttribute. You can see in Line 3 that this class is restricted to All, which really means that it isn’t restricted—it can be used at all the locations specified in Listing 21.3. You can see that AttributeUsage is an attribute that is passed one positional parameter. You know that it is an attribute because it is enclosed in square brackets. The attribute class actually starts in Line 4. As you can see, the CodeStatusAttribute class inherits from System.Attribute and thus is an attribute class. The rest of the class con- tains standard code that you should be able to follow. Three private variables are all accessed using properties. ANALYSIS A Day for Reflection and Attributes 705 21 You should note a few things. Parameters that are defined as part of the constructor are positional. The first parameter used in the CodeStatusAttribute attribute is the Status parameter. Two named parameters are also available: the other two public members, Coder and Tester. Using a Custom Attribute Now that you’ve defined an attribute, you’ll want to use it. Using the CodeStatusAttribute attribute in a listing is done just as the attributes used earlier were used. You need to include the positional parameter, and you have the option of including the named para- meters. Listing 21.4 presents a code fragment with several classes. These classes use the CodeStatusAttribute attribute to indicate the status of the coding efforts. This is not a complete listing, so it won’t compile correctly. Caution LISTING 21.4 attrUsed.cs—CodeStatusAttribute in Use with a Class 1: // attrUsed.cs - using the CodeStatus attribute 2: // 3: 4: [CodeStatus(“Beta”, Coder=”Brad”)] 5: public class Circle 6: { 7: public Circle() 8: { 9: // Set up and build a circle class 10: } 11: } 12: 13: [CodeStatus(“Final”, Coder=”Fred”, Tester=”John”)] 14: public class Square 15: { 16: public Square() 17: { 18: // Set up and build a square class 19: } 20: } 21: 22: [CodeStatus(“Alpha”)] 23: public class Triangle 24: { 25: public Triangle() 26: { 27: // Set up and build a triangle class 28: } 29: } 30: 31: [CodeStatus(“Final”, Coder=”Bill”)] 32: public class Rectangle 33: { 34: public Rectangle() 35: { 36: // Set up and build a rectangle class 37: } 38: } This class uses the CodeStatusAttribute attribute. You might wonder whether there is an error in Lines 4, 13, 22, and 31. These lines use CodeStatus instead of CodeStatusAttribute. This is not an error. You can change all of these to CodeStatusAttribute and the program will work; however, you don’t have to. The .NET Framework enables you to define attributes with the word Attribute at the end of the name. When you do use the attribute, you can drop the word Attribute. This helps make your listings a little more readable, and it makes your attribute definitions easy to identify. In Line 4, the CodeStatus attribute is called with the positional attribute parameter filled with “Beta”, and one named parameter is used, Coder. It is assigned the value “Brad”. In Line 13, you can see that a Tester named parameter is also included. Line 21 contains the minimum parameters—it contains only a positional value. Accessing the Associated Attribute Information If you couldn’t access the attribute information at runtime, there would be little point of using attributes. You can access the attribute information via reflection. Listing 21.5 pre- sents the code that can be used to see what attributes are associated with a class. LISTING 21.5 reflAttr.cs—Reflection on the CodeStatus Attributes 1: // reflAttr.cs - 2: // 3: class reflAttr 4: { 5: public static void Main() 6: { 7: PrintAttributes(typeof(Rectangle)); 8: } 9: 706 Day 21 LISTING 21.4 continued ANALYSIS A Day for Reflection and Attributes 707 21 10: public static void PrintAttributes(Type psdType ) 11: { 12: Console.WriteLine(“\nAttributes for: {0}”, psdType.ToString()); 13: 14: Attribute[] attribs = Attribute.GetCustomAttributes(psdType); 15: foreach (Attribute attr in attribs) 16: { 17: CodeStatus item = (CodeStatus) attr; 18: Console.WriteLine( 19: “Status is {0}. Coder is {1}. Tester is {2}.”, 20: item.ToString(), item.Coder, item.Tester); 21: } 22: } 23: } This code snippet enables you to evaluate what attributes are associated with a class. The bulk of the listing is in the PrintAttributes method in Lines 10–22. This method takes a type and then prints the types associated with that type. The Main method of this code snippet shows how the PrintAttributes method can be called with the Rectangle class. You should remember from Day 6, “Packaging Functionality: Class Methods and Member Functions,” that a class is itself a type. This means that a Rectangle object is a Rectangle type. Because you can’t pass the name, a method from the .NET Framework is used to convert the class name to a Type. This is the typeof operator. First, in the PrintAttributes method, the name of the type passed into the method, psdType, is printed. For the Rectangle class, this is Rectangle. In Line 14, an array of type Attribute is created. This array is named attribs. It is assigned the value of the attributes from the type that was passed into the method. This is done using a method within the Attribute class named GetCustomAttributes, which returns the individual attributes associated with the argument. In the case of psdType, which contains the Rectangle type, there was one attribute (in Line 31 of Listing 21.4). If there were additional attributes, they would be assigned to this array as well. In Lines 15–21, a foreach statement is used to loop through the attribs array of Attribute values. A variable named attr is defined as a single Attribute in Line 15 as a part of the foreach statement. This is assigned the current value from the attribs array. For each Attribute in the array (attr), the three possible parameter values are printed. This is done by first changing the current attr value to be a CodeStatus value using cast- ing (in Line 17). If Line 15 is confusing, review the inheritance lessons in Days 10, “Reusing Existing Code with Inheritance,” and 12, “Tapping into OOP: Interfaces.” When you have cast the attr to a CodeStatus, you can then use the methods, properties, and fields as if it were a normal CodeStatus type (which it is). LISTING 21.5 continued ANALYSIS Pulling It All Together Up to this point, you have seen all the parts of creating an attribute, associating it with your classes, and getting the information at runtime. Listing 21.6 pulls this all together into a listing that can be compiled and executed. You’ll see that this listing is composed of the previous three listings and nothing more. LISTING 21.6 complete.cs—Using a Custom Attribute 1: // complete.cs - 2: // 3: using System; 4: 5: [AttributeUsage(AttributeTargets.All)] 6: public class CodeStatusAttribute : System.Attribute 7: { 8: private string pSTATUS; 9: private string pTESTER; 10: private string pCODER; 11: 12: public CodeStatusAttribute( string Status ) 13: { 14: this.pSTATUS = Status; 15: } 16: 17: public string Tester 18: { 19: set 20: { 21: pTESTER = value; 22: } 23: get 24: { 25: return pTESTER; 26: } 27: } 28: 29: public string Coder 30: { 31: set 32: { 33: pCODER = value; 34: } 35: get 36: { 37: return pCODER; 38: } 39: } 40: 708 Day 21 A Day for Reflection and Attributes 709 21 41: public override string ToString() 42: { 43: return pSTATUS; 44: } 45: } 46: 47: // attrUsed.cs - using the CodeStatus attribute 48: // 49: 50: [CodeStatus(“Beta”, Coder=”Brad”)] 51: public class Circle 52: { 53: public Circle() 54: { 55: // Set up and build a circle class 56: } 57: } 58: 59: [CodeStatus(“Final”, Coder=”Fred”, Tester=”John”)] 60: public class Square 61: { 62: public Square() 63: { 64: // Set up and build a square class 65: } 66: } 67: 68: [CodeStatus(“Alpha”)] 69: public class Triangle 70: { 71: public Triangle() 72: { 73: // Set up and build a triangle class 74: } 75: } 76: 77: [CodeStatus(“Final”, Coder=”Bill”)] 78: public class Rectangle 79: { 80: public Rectangle() 81: { 82: // Set up and build a rectangle class 83: } 84: } 85: 86: class reflAttr 87: { 88: public static void Main() 89: { LISTING 21.6 continued 90: PrintAttributes(typeof(Circle)); 91: PrintAttributes(typeof(Triangle)); 92: PrintAttributes(typeof(Square)); 93: PrintAttributes(typeof(Rectangle)); 94: } 95: 96: public static void PrintAttributes( Type psdType ) 97: { 98: Console.WriteLine(“\nAttributes for: {0}”, psdType.ToString()); 99: 100: Attribute[] attribs = Attribute.GetCustomAttributes(psdType); 101: foreach (Attribute attr in attribs) 102: { 103: CodeStatusAttribute item = (CodeStatusAttribute) attr; 104: Console.WriteLine( 105: “Status is {0}. Coder is {1}. Tester is {2}.”, 106: item.ToString(), item.Coder, item.Tester); 107: } 108: } 109: } Attributes for: Circle Status is Beta. Coder is Brad. Tester is . Attributes for: Triangle Status is Alpha. Coder is . Tester is . Attributes for: Square Status is Final. Coder is Fred. Tester is John. Attributes for: Rectangle Status is Final. Coder is Bill. Tester is . The first part of the listing defines the custom attribute CodeStatusAttribute. This attribute is then used with its shortened name, CodeStatus, with the classes throughout the middle part of the listing. Finally, the reflAttr class checks the attributes on each of the classes. Lines 90–91 are additions. In the previous listing, only the Rectangle class was included. In this listing, each of the different class types is used with the PrintAttributes method. The output shows that the appropriate attributes are printed for each. 710 Day 21 LISTING 21.6 continued OUTPUT ANALYSIS A Day for Reflection and Attributes 711 21 Single-Use Versus Multiuse Attributes One other point regarding attributes deserves some attention. If you try to associate the CodeStatus with the same class more than once, you will get an error. For example, con- sider the following: [CodeStatus(“Beta”, Coder=”Brad”)] [CodeStatus(“Testing”, Tester=”Bill”)] class Rectangle() This generates an error. However, what if you changed the attribute to be information on the coder of the class? The attribute could contain the coder’s name as a positional para- meter. Additional named parameters could include information such as the last date mod- ified or the status. It would make sense that you could then have multiple coders on a single class. Using multiple attributes of the same type on an item is simple. All you need to do is specify that multiple associates are allowed when you initially declare the attribute. When you declared an attribute earlier, you included the following information as an attribute on your attribute declaration: [AttributeUsage(AttributeTargets)] Here, AttributeTargets is a positional parameter that specifies the valid targets for your attribute. You can also include the AllowMultiple named parameter. Setting this parameter to true enables you to use the same attribute multiple times on the same target. Although the default is false, you can state this value by assigning false to AllowMultiple. To allow multiple CodeStatus attributes to be used, you change a single line in the com- plete.cs listing. Changing Line 5 to the following is all that is required: 5: [AttributeUsage(AttributeTargets.All, AllowMultiple=true)] When you’ve done this, you can add multiple CodeStatus attributes to your listing. Although this is all included in a single listing, you could have included the custom attribute with a using statement in a separate file or namespace. Note Reflecting on the Future of C# Although the C# programming language has been standardized by ECMA and ISO, it is not locked in stone. A number of features are being considered as enhancements to the language. These are changes—or enhancements—that will make C# an even more pow- erful language. Most of these changes are advanced features to the language. Four big changes that may happen are listed here: • Generics • Iterators • Partial types • Anonymous methods At the time this book was written, these features were not a part of the current standard for C#, nor had they been incorporated into any public products. However, these are being considered for a future standard. 712 Day 21 The coverage provided here is not intended to be complete coverage. Rather, it is provided to help make you aware of future changes to C#. Note Generics Generics are used to help make the code in your software components much more reusable. Generics are a type of data structure that contains code that remains the same; however, the data type of the parameters can change with each use. Additionally, the usage within the data structure adapts to the different data type of the passed variables. In summary, a generic is a code template that can be applied to use the same code repeatedly. Each time the generic is used, it can be customized for different data types without needing to rewrite any of the internal code. The functionality that is provided by generics can be obtained in C# today. This func- tionality is done by using type casts and polymorphism, similar to what you learned about on Day 12. With generics, however, you can avoid the messy and intensive conver- sions from reference types to native types. Additionally, you can create routines that are much more type-safe. A generic is defined using a slightly different notation. The following is the basic code for a generic named Compare that can compare two items of the same type and return the larger or smaller value, depending on which method is called: [...]... Listing 22.2 to use the FieldInfo type instead of the MemberInfo type Use this type to evaluate a listing for its field values 4 Modify the complete.cs listing to allow multiple attributes to be assigned to a single target Assign two CodeStatus attributes to a single class 21 WEEK 3 15 Week in Review 16 Congratulations! You have come to the end of this book In addition to learning the fundamentals of C#, ... do this the appropriate way—is with a constraint A constraint is a class or interface that must be included as a part of the type used for the parameter For example, in the previous Compare class, to make sure that any data type will work as a parameter when declaring the delegate, you can force the data types to have implemented the IComparable interface from the NET Framework 21 714 Day 21 You can... the icon that should be used for the output Command-Line Compiler Flags for Microsoft Visual C# NET 737 /resource: or /res: This flag embeds the specified resource /linkresource: or /linkres: This flag links the specified resource to this assembly Code Generation /debug[+|-] This flag indicates whether debugging information should be included (+) or omitted (-) /debug:{full|pdbonly}... LISTING 21. 7 715 FileOne.cs— Not a Complete Listing public class partial MyClass { // Class stuff public void FunctionInMyClass() { // Logic } // more stuff } LISTING 21. 8 FileTwo.cs—Not a Complete Listing public class partial MyClass { // Class stuff public void AntherFunctionInMyClass() { // Logic } // more stuff } When these listings are compiled together, the logic is combined into a single... Web site 20 21 A Appendices B A C# Keywords B Command-Line Compiler Flags for Microsoft Visual C# NET C C Understanding Number Systems D Using SharpDevelop Answers Answers are located on the CD-ROM D APPENDIX A C# Keywords Keywords have specific meanings and use, and are reserved in the C# language The following are C# keywords: abstract A modifier that can be used to indicate that a class is to be... allocated in the try block fixed A keyword used within unmanaged code to lock a reference type in memory so that the garbage collector won’t move it C# Keywords float A data type that stores a floating-point number in 4 bytes The suffixes f and F designate a float literal float is equivalent to System.Single in the NET Framework for A program flow statement used for looping This statement contains an initializer,... array that the foreach will loop through int A data type that stores a signed integer in 4 bytes The range of possible values is from –2,147,483,648 to 2,147,483,647 int is equivalent to System.Int32 in the NET Framework Literal numbers with no suffix are of type int by default if the value fits within the given range for an int interface A keyword used to declare a reference type that defines a set... modifier that indicates that a method, property, or other member of a class or structure is accessible readonly A data member modifier that indicates that after the initial assignment—either at the time of declaration or within the constructor the value within the data member cannot be changed ref A parameter modifier that indicates that changes to the parameter variable will also be reflected in the variable... C# Keywords uint A data type that stores an unsigned integer in 4 bytes The range of possible values is from 0 to 4,294,967,295 uint is equivalent to System.UInt32 in the NET Framework A suffix of u or U designates a uint literal ulong A data type that stores an unsigned integer in 8 bytes The range of possible values is from 0 to 18,446,744,073,709,551,615 ulong is equivalent to System.UInt64 in the. .. used within iterators to indicate a value that should be returned to a foreach statement The yield keyword also indicates where the foreach statement should continue on its next iteration APPENDIX B Command-Line Compiler Flags for Microsoft Visual C# NET You can set options with the Microsoft Visual C# NET command-line compiler You can see the options by running the command-line compiler with the /help . printed. This is done by first changing the current attr value to be a CodeStatus value using cast- ing (in Line 17). If Line 15 is confusing, review the inheritance lessons in Days 10, “Reusing. classes throughout the middle part of the listing. Finally, the reflAttr class checks the attributes on each of the classes. Lines 90–91 are additions. In the previous listing, only the Rectangle class was included. In. 703 21 These values in this enumeration are the flags listed in Table 21. 3. To include more than one attribute restriction from the table, you use the | operator. The following shows how to use the