Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]

.pdf
Скачиваний:
71
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

172 CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

public static string Company

{

get { return companyName; } set { companyName = value; }

}

...

}

Static properties are manipulated in the same manner as static methods, as shown here:

// Interact with the static property. static void Main(string[] args)

{

Console.WriteLine("***** Fun with Encapsulation *****\n");

// Set company.

Employee.Company = "Intertech Training"; Console.WriteLine("These folks work at {0}.", Employee.Company);

Employee emp = new Employee("Marvin", 24, 456, 30000, "111-11-1111"); emp.GiveBonus(1000);

emp.DisplayStats();

Console.ReadLine();

}

Finally, recall that classes can support static constructors. Thus, if you wanted to ensure that the name of the static companyName field was always assigned to “Intertech Training,” you would write the following:

// Static constructors are used to initialize static data. public class Employee

{

private Static companyName As string

...

static Employee()

{

companyName = "Intertech Training";

}

}

Using this approach, there is no need to explicitly call the Company property to set the initial value:

// Automatically set to "Intertech Training" via static constructor. static void Main(string[] args)

{

Console.WriteLine("These folks work at {0}", Employee.Company);

}

To wrap up the examination of encapsulation using C# properties, understand that these syntactic entities are used for the same purpose as traditional accessor (get)/mutator (set) methods. The benefit of properties is that the users of your objects are able to manipulate the internal data point using a single named item.

Note In Chapter 13 you will examine a new C# 2008 construct called automatic properties. This feature allows you to define a property definition and the related private member variable using a very concise syntax.

CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

173

Understanding Constant Data

Now that you can create fields that can be modified using type properties, allow me to illustrate how to define data that can never change after the initial assignment. C# offers the const keyword to define constant data. As you might guess, this can be helpful when you are defining a set of known values for use in your applications that are logically connected to a given class or structure.

Turning away from the Employee example for a moment, assume you are building a utility class named MyMathClass that needs to define a value for the value PI (which we will assume to be 3.14). Begin by creating a new Console Application project named ConstData. Given that we would not want to allow other developers to change this value in code, PI could be modeled with the following constant:

namespace ConstData

{

class MyMathClass

{

public const double PI = 3.14;

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Fun with Const *****\n");

Console.WriteLine("The value of PI is: {0}", MyMathClass.PI);

// Error! Can't change a constant!

MyMathClass.PI = 3.1444;

Console.ReadLine();

}

}

}

Notice that we are referencing the constant data defined by MyMathClass using a class name prefix (i.e., MyMathClass.PI). This is due to the fact that constant fields of a class or structure are implicitly static. However, it is permissible to define and access a local constant variable within a type member. By way of example:

static void LocalConstStringVariable()

{

//A local constant data point can be directly accessed. const string fixedStr = "Fixed string Data"; Console.WriteLine(fixedStr);

//Error!

fixedStr = "This will not work!";

}

Regardless of where you define a constant piece of data, the one point to always remember is that the initial value assigned to the constant must be specified at the time you define the constant. Thus, if you were to modify your MyMathClass in such a way that the value of PI is assigned in a class constructor as follows:

174 CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

class MyMathClass

{

// Try to set PI in ctor? public const double PI;

public MyMathClass()

{

// Error!

PI = 3.14;

}

}

you would receive a compile-time error. The reason for this restriction has to do with the fact the value of constant data must be known at compile time. Constructors, as you know, are invoked at runtime.

Understanding Read-Only Fields

Closely related to constant data is the notion of read-only field data (which should not be confused with a read-only property). Like a constant, a read-only field cannot be changed after the initial assignment. However, unlike a constant, the value assigned to a read-only field can be determined at runtime, and therefore can legally be assigned within the scope of a constructor (but nowhere else).

This can be very helpful when you don’t know the value of a field until runtime (perhaps because you need to read an external file to obtain the value), but wish to ensure that the value will not change after that point. For the sake of illustration, assume the following update to MyMathClass:

class MyMathClass

{

//Read-only fields can be assigned in ctors,

//but nowhere else.

public readonly double PI; public MyMathClass ()

{

PI = 3.14;

}

}

Again, any attempt to make assignments to a field marked readonly outside the scope of a constructor results in a compiler error:

class MyMathClass

{

public readonly double PI; public MyMathClass ()

{

PI = 3.14;

}

// Error!

public void ChangePI() { PI = 3.14444;}

}

CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

175

Static Read-Only Fields

Unlike a constant field, read-only fields are not implicitly static. Thus, if you wish to expose PI from the class level, you must explicitly make use of the static keyword. If you know the value of a static read-only field at compile time, the initial assignment looks very similar to that of a constant:

class MyMathClass

{

public static readonly double PI = 3.14;

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Fun with Const *****");

Console.WriteLine("The value of PI is: {0}", MyMathClass.PI); Console.ReadLine();

}

}

However, if the value of a static read-only field is not known until runtime, you must make use of a static constructor as described earlier in this chapter:

class MyMathClass

{

public static readonly double PI;

static MyMathClass() { PI = 3.14; }

}

Now that we have examined the role of constant data and read-only fields, we can return to the Employee example and put the wraps on this chapter.

Source Code The ConstData project is included under the Chapter 5 subdirectory.

Understanding Partial Types

Classes and structures can be defined with a type modifier named partial that allows you to define a type across multiple *.cs files. Earlier versions of the language required all code for a given type be defined within a single *.cs file. Given the fact that a production-level C# class may be hundreds of lines of code (or more), this can end up being a mighty lengthy file indeed.

In these cases, it may be beneficial to partition a type’s implementation across numerous *.cs files in order to separate code that is in some way more important from other aspects of the type definition. For example, using the partial class modifier, you could place all of the Employee constructors and properties into a new file named Employee.Internals.cs:

partial class Employee

{

//Constructors

...

//Properties

...

}

176CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

while the private field data and type methods are defined within the initial Employee.cs:

partial class Employee

{

// Field data.

private string empName; private int empID; private float currPay; private int empAge; private string empSSN;

private static string companyName;

public void GiveBonus(float amount)

{

currPay += amount;

}

public void DisplayStats()

{

Console.WriteLine("Name: {0}", empName); Console.WriteLine("ID: {0}", empID); Console.WriteLine("Age: {0}", empAge); Console.WriteLine("SSN: {0}", empSSN); Console.WriteLine("Pay: {0}", currPay);

}

}

As you might guess, this can be helpful to new team members who need to quickly learn about the public interface of the type. Rather than reading through a single (lengthy) C# file to find the members of interest, they can focus on the public members. Of course, once these files are compiled by the C# compiler, the end result is a single unified type. To this end, the partial modifier is purely a design-time construct.

Also know that the names you give to the files that contain partial type definitions are entirely up to you. Here, Employee.Internal.cs was chosen simply to indicate that this file contains grungy infrastructure code that most developers can ignore. The only requirement when defining partial types is that the type’s name (Employee in this case) is identical and defined within the same .NET namespace.

Note Visual Studio 2008 makes use of the partial keyword to partition code generated by the IDE’s designer tools (such as various GUI designers). Using this approach, you can keep focused on your current solution, and be blissfully unaware of the designer-generated code.

Documenting C# Source Code via XML

The final task of this chapter is to examine a specific way to comment your code that yields XMLbased code documentation. If you have worked with the Java programming language, you may be familiar with the javadoc utility. Using javadoc, you are able to turn Java source code into a corresponding HTML representation (provided the *.java file contains the correct code comment syntax). The C# documentation model is slightly different, in that the code-comments-to-XML conversion process is the job of the C# compiler (via the /doc option) rather than a stand-alone utility.

CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

177

So, why use XML to document our type definitions rather than HTML? The main reason is that XML is a very “enabling technology.” Given that XML separates the definition of data from the presentation of that data, we can apply any number of XML transformations to the underlying XML to display the code documentation in a variety of formats (MSDN format, HTML, etc.).

When you wish to document your C# types in XML, your first step is to make use of the new triple slash (///) code comment notations. Once a documentation comment has been declared, you are free to use any well-formed XML elements, including the recommended set shown in Table 5-2.

Table 5-2. Recommended Code Comment XML Elements

Predefined XML Documentation

 

Element

Meaning in Life

<c>

Indicates that the following text should be displayed in a specific

 

“code font”

<code>

Indicates multiple lines should be marked as code

<example>

Mocks up a code example for the item you are describing

<exception>

Documents which exceptions a given class may throw

<list>

Inserts a list or table into the documentation file

<param>

Describes a given parameter

<paramref>

Associates a given XML tag with a specific parameter

<permission>

Documents the security constraints for a given member

<remarks>

Builds a description for a given member

<returns>

Documents the return value of the member

<see>

Cross-references related items in the document

<seealso>

Builds an “also see” section within a description

<summary>

Documents the “executive summary” for a given member

<value>

Documents a given property

 

 

If you are making use of the new C# XML code comment notation, do be aware the Visual Studio 2008 IDE will generate documentation skeletons on your behalf. For example, if you add a triple slash above the definition of your Employee class, you end up with the following skeleton:

///<summary>

///</summary> partial class Employee

{

...

}

Simply fill in the blanks with your custom content:

///<summary>

///This class represents an Employee.

///</summary>

partial class Employee

{

...

}

178 CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

By way of another example, insert a triple slash code comment to your custom five-argument constructor. This time the comment builder utility has been kind enough to add <param> elements:

///<summary>

///</summary>

///<param name="name"></param>

///<param name="age"></param>

///<param name="id"></param>

///<param name="pay"></param>

///<param name="ssn"></param>

public Employee(string name, int age, int id, float pay, string ssn)

{

empName = name; empID = id; empAge = age; currPay = pay; empSSN = ssn;

}

Also be aware that these XML code comments can be entered using the Class Details window (see Chapter 2) of Visual Studio 2008, as shown in Figure 5-11.

Figure 5-11. Entering XML comments using the Class Details window

One benefit of annotating your code with XML comments is that you are able to view this information from within Visual Studio’s IntelliSense (see Figure 5-12). As you would guess, this can be helpful to other members on your team who might not know the role of a given type member.

Figure 5-12. XML comments are viewable via IntelliSense.

CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

179

Generating the XML File

In any case, once you have documented your code with XML comments, the next step is to generate a corresponding *.xml file based on the XML data. If you are building your C# programs using the command-line compiler (csc.exe), the /doc flag is used to generate a specified *.xml file based on your XML code comments:

csc /doc:XmlCarDoc.xml *.cs

Visual Studio 2008 projects allow you to specify the name of an XML documentation file using the Generate XML documentation file check box option found on the Build tab of the Properties window (see Figure 5-13).

Figure 5-13. Generating an XML code comment file via Visual Studio 2008

Once you have enabled this behavior, the compiler will place the generated *.xml file within your project’s \bin\Debug folder. You can verify this for yourself by clicking the Show All Files button on the Solution Explorer, generating the result in Figure 5-14.

Figure 5-14. Locating the generated XML documentation file

180 CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

Note There are many other elements and notations that may appear in C# XML code comments. If you are interested in more details, look up the topic “XML Documentation Comments (C# Programming Guide)” within the

.NET Framework SDK 3.5 documentation.

Transforming XML Code Comments via NDoc

Now that you have generated an *.xml file that contains your source code comments, you may be wondering exactly what to do with it. Sadly, Visual Studio 2008 does not provide a built-in utility that transforms XML data into a more user-friendly help format (such as an HTML page). If you are comfortable with the ins and outs of XML transformations, you are, of course, free to manually create your own style sheets.

A simpler alternative, however, are the numerous third-party tools that will translate an XML code file into various helpful formats. For example, recall from Chapter 2 that the NDoc application generates documentation in several different formats. Assuming you have this tool installed, the first step is to specify the location of your *.xml file and the corresponding assembly. To do so, click the Add button of the NDoc GUI. This will open the dialog box shown in Figure 5-15.

Figure 5-15. Specifying the XML file and corresponding assembly

At this point, you can select for output location (via the OutputDirectory property) and document type (via the Documentation Type drop-down list). For this example, let’s pick an MSDNCHM format, which will generate documentation that looks and feels identical to the .NET Framework 3.5 documentation (see Figure 5-16).

CHAPTER 5 DEFINING ENCAPSULATED CLASS TYPES

181

Figure 5-16. Specifying the output directory and documentation format

Obviously, we could establish other settings using the NDoc GUI, however once you select the Documentation Build menu option, NDoc will generate a full help system for your application. Figure 5-17 shows the end result.

Figure 5-17. Our MSDN-style help system for the EmployeeApp project