Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Teach Yourself The CSharp Language In 21 Days (2004) [eng].pdf
Скачиваний:
43
Добавлен:
16.08.2013
Размер:
8.5 Mб
Скачать

A Day for Reflection and Attributes

705

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 parameters. Listing 21.4 presents a code fragment with several classes. These classes use the CodeStatusAttribute attribute to indicate the status of the coding efforts.

Caution

This is not a complete listing, so it won’t compile correctly.

 

LISTING 21.4 attrUsed.cs—CodeStatusAttribute in Use with a Class

1:// attrUsed.cs - using the CodeStatus attribute

2://-----------------------------------------------

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”)]

21

23:

public class Triangle

24:{

25:public Triangle()

26:{

27:// Set up and build a triangle class

ANALYSIS

706

Day 21

LISTING 21.4 continued

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 presents 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:

ANALYSIS

A Day for Reflection and Attributes

707

LISTING 21.5 continued

10:public static void PrintAttributes(Type psdType )

11:{

12:Console.WriteLine(“\nAttributes for: {0}”, psdType.ToString());

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- 21 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).

708

Day 21

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:

A Day for Reflection and Attributes

LISTING 21.6 continued

41:public override string ToString()

42:{

43:return pSTATUS;

44:}

45:}

46:

47:// attrUsed.cs - using the CodeStatus attribute

48://-----------------------------------------------

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:{

709

21

OUTPUT
ANALYSIS

710

Day 21

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());

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.

A Day for Reflection and Attributes

711

Note

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.

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, consider 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 parameter. Additional named parameters could include information such as the last date modified 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 complete.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.

21

712

Day 21

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 powerful 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.

Note

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#.

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 functionality 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 conversions 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:

A Day for Reflection and Attributes

713

public class Compare<ItemType, ItemType>

{

public ItemType Larger(ItemType data, ItemType data2)

{

// logic...

}

public ItemType Smaller(ItemType data, ItemType data2)

{

// logic...

}

}

Note

This is incomplete code; however, the important part is shown.

This generic could be used with any data type, ranging from basic data types such as integers to complex classes and structures. When you use the generic, you identify what data type you are using with it. For example, to use an integer with the previous Compare generic, you would enter code similar to the following:

Compare<int, int> compare = new Compare<int, int>; int MyInt = compare.Larger(3, 5);

You could use the type with other types as well. One thing to be aware of is that a declared generic such as the Compare in the previous example is strongly typed. This means if you pass a different data type than an integer to compare.Larger, the compiler will display an error. If you wanted to use a different data type, you would need to declare another instance of the generic:

Compare<float, float> f_compare = new Compare<float, float>; float MyFloat = f_compare.Larger(1.23f, 4.32f);

Because you can use this with different types, you don’t need to change the original generic code.

The example here is a simplification of what can be done with generics. You will find

 

that to truly create a generic type that can be used with any data type as a parameter, you

 

will need to ensure that a number of requirements are met. One way to do this—the

 

appropriate way—is with a constraint. A constraint is a class or interface that must be

21

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.

714

Day 21

You can add a constraint by including it after the generic class declaration. You indicate a constraint using the new keyword where:

public class Compare<ItemType, ItemType> where ItemType : IComparable

{

public ItemType Larger(ItemType data, ItemType data2)

{

// logic...

}

public ItemType Smaller(ItemType data, ItemType data2)

{

// logic...

}

}

What Are Iterators?

An iterator is a construct that helps a foreach statement loop through a class. Although this can be done with C# today, it requires a number of complex pieces of code to be included. With the new standard, the inclusion of an additional keyword, yield, is being added to simplify the capability to add iterators to your data types.

What Are Partial Types?

Partial types are being added to allow a single class to be defined in more than one file. Although it is recommended that a class be stored in a single source file, sometimes that is just not practical. You also can rewrite the class to inherit some of the code from a subclass, but this also is not always practical.

Partial types resolve this by allowing multiple files to be used to declare a single class. When compiled, the classes can be combined so that a single class is created.

Note

If you use a tool to generate some of your code, you can use partial types to combine the generated code with your own additions to the class. You can keep both pieces in separate files. You won’t have to worry about inadvertently changing some of the generated code.

Partial types are implemented using yet another new keyword, partial. The partial keyword is added to the class declaration in each file that will be combined. For example, the following declares a class named MyClass within parts in two different source files named FileOne.cs and FileTwo.cs:

A Day for Reflection and Attributes

715

LISTING 21.7 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 class.

What Are Anonymous Methods?

Anonymous methods allow snippets of code to be created that can be called dynamically at a later time. These methods are similar to delegates that you learned about on Day 13, “Making Your Programs React with Delegates, Events, and Indexers.” They differ from delegates in that the code for the method is included as a part of the delegate declaration instead of as a separate method. This provides the benefit of not having to declare a separate method for the delegate.

Anonymous methods can access other variables within the same class they are located, yet outside their own declaration. Additionally, anonymous methods can receive parameters.

Summary

21

In this last day, you covered two advanced topics within C# programming. Both enable you to get technical programming information at runtime. First you discovered reflection.

716

Day 21

You learned that through reflection, you can learn what methods, properties, events, and other members are available within a program.

After discovering reflection, you learned about attributes. Attributes enable the C# language to be extended in a structured manner. Additionally, attributes enable you to associate additional information to portions of your programs. You learned how to create custom attributes. You learned how to associate them with your own classes. Finally, you learned how to query the information about attributes on a program at runtime.

The day’s lesson ended with just a brief overview of some of the features being considered in the future of C#. This included a brief mention of generics, iterators, partial types, and anonymous methods. This also mentioned the addition of three new keywords:

partial, yield, and where.

Congratulations!

Congratulations, you’ve made it through 21 days of C#. You’ve learned a lot in just 21 lessons. There is more to learn—like today’s topics, most of what you could continue to learn is an extension of what you already know. Attributes are just another application of classes within your listings. Reflection uses classes and information in the Base Class Library. If you understood most of what was in this book, you are ready to tackle almost any basic C# project. The best way to become an expert or guru is to apply what you’ve learned. Write programs. The more programs you write, the quicker you will most likely go from simply knowing C# to being a full-fledged expert.

Q&A

QIf an attribute is supposed to appear before the element it is associated with, where do you put an attribute associated to an assembly?

AAttributes for assemblies and modules are placed in your code listings after your using clauses for namespaces and before your code. This is the only location they can go for an assembly.

Q Can reflection be used with attributes?

A Reflection is used to determine attribute values.

Q Can I use generics, iterators, partial types, and anonymous methods today?

AThe versions of C# available at the time this book was written did not support these new features, nor were they a part of the initial C# standards. However, Microsoft has publicly stated that these features will be submitted for standardization. You should check your C# compiler’s documentation to see whether these

A Day for Reflection and Attributes

717

features are supported yet. Microsoft is planning to support the features in the version of Visual Studio .NET after Visual Studio .NET 2003.

Workshop

The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you’ve learned. Answers are provided on the CD.

Quiz

1.What can be used to get the type of an object, class, or other item?

2.What type can be used to hold a type value? What namespace is this type in?

3.What concept provides information about a class at runtime?

4.What type would you use to get detailed information on a method’s parameter(s)?

5.What has been included in C# to help the language be expanded in the future or to help the language handle concepts not currently discovered?

6.Was the WebMethod tag that you used in creating Web services on Day 16 an example of reflection or an example of attributes?

7.Name three predefined attributes.

8.What five things within a program can an attribute be associated with?

9.What two types of parameters are used with attributes? What is the difference between the two?

10.How can you limit what items an attribute can be assigned to?

11.Bonus: What data types can an attribute parameter be?

Exercises

1.Modify the MyMemberInfo.cs listing (Listing 21.1) to reflect on the Object class

(System.Object).

2.What methods are available in the Object class? Which methods in the Object type are also in the types you displayed in today’s exercises?

3.Modify Listing 22.2 to use the FieldInfo type instead of the MemberInfo type. Use

this type to evaluate a listing for its field values.

21

 

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.

WEEK 3

Week in Review

Congratulations! You have come to the end of this book. In addition to learning the fundamentals of C#, you learned to use some of the classes and other types within the standard class libraries, including the Base Class Libraries (BCL).

You learned that by using pre-existing classes, you have the capability to create applications that are windows-based, Web-based, or services-based. You learned that you can use a number of existing classes to instantly gain large amounts of functionality.

Apply What You Know

The best way to ensure that you have learned C# is to apply what you have learned. You should create as many C# programs as you can. You will find that the more you use C#, the easier it becomes.

Show What You Know

As you continue to use C#, you will most likely create a number of programs or classes that you are proud of or that you believe will be useful to others. If so, I recommend that you share your code with others. You can send a copy of your listing along with a couple of paragraphs of text detailing what your listing does and what is special about it. Send copies to sites such as www.CodeGuru.com, which will post your listing so that thousands of others can also see and use it. At the same time, you will be able to review the listings that have been submitted by others to these sites. For specific submission guidelines, see the Web site.

15

16

17

18

19

20

21