Defining Class Members

>> Aug 19, 2009

This chapter continues exploring class definitions in C# by looking at how you define field, property, and method class members. You start by examining the code required for each of these types, and learn how to generate the structure of this code using wizards. You also learn how to modify members quickly by editing their properties.
After covering the basics of member definition, you ’ ll learn some advanced techniques involving members: hiding base class members, calling overridden base class members, nested type definitions, and partial class definitions.
Finally, you put theory into practice by creating a class library that you can build on and use in later chapters.
In this chapter you learn how to do the following:
. Work with fields, properties, and method class members .
. Create a class library.

Member Definitions
Within a class definition, you provide definitions for all members of the class, including fields, methods, and properties. All members have their own accessibility levels, defined in all cases by one of the following keywords:
. public— Members are accessible from any code.
. private— Members are accessible only from code that is part of the class (the default if no keyword is used).
. internal— Members are accessible only from code within the project (assembly) where they are defined.
. protected— Members are accessible only from code that is part of either the class or a derived class.

The last two of these can be combined, so protected internal members are also possible. These are only accessible from code - derived classes within the project (more accurately, the assembly).
Fields, methods, and properties can also be declared using the keyword static, which means that they are static members owned by the class, rather than by object instances, as discussed in Chapter 8 .

Defining Fields
Fields are defined using standard variable declaration format (with optional initialization), along with the modifiers discussed previously:
class MyClass
{
public int MyInt;
}


Public fields in the .NET Framework are named using PascalCasing, rather than camelCasing, and that ’s the casing methodology used here. That ’s why the field in this example is called MyIntinstead of myInt. This is only a suggested casing scheme, but it makes a lot of sense. There is no recommendation for private fields, which are usually named using camelCasing.
Fields can also use the keyword readonly, meaning that the field may be assigned a value only during constructor execution or by initial assignment:
class MyClass
{
public readonly int MyInt = 17;
}


As noted in the chapter introduction, fields may be declared as static using the statickeyword:
class MyClass
{
public static int MyInt;
}


Static fields are accessed via the class that defines them ( MyClass.MyInt in the preceding example), not through object instances of that class. You can use the keyword const to create a constant value. const members are static by definition, so you don ’ t need to use the static modifier (in fact, it is an error).

Defining Methods
Methods use standard function format, along with accessibility and optional staticmodifiers, as shown in this example:
class MyClass
{
public string GetString()
{
return “Here is a string.”;
}
}


Like fields, public methods in the .NET Framework are named using PascalCasing.
Remember that if you use the static keyword, then this method is accessible only through the class, not the object instance. You can also use the following keywords with method definitions:
. virtual— The method may be overridden.
. abstract— The method must be overridden in non - abstract derived classes (only permitted in abstract classes).
. override— The method overrides a base class method (it must be used if a method is being overridden).
. extern— The method definition is found elsewhere.

Here ’ s an example of a method override:
public class MyBaseClass
{
public virtual void DoSomething()
{
// Base implementation.
}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
// Derived class implementation, overrides base implementation.
}
}


If overrideis used, then sealedmay also be used to specify that no further modifications can be made to this method in derived classes — that is, the method can ’ t be overridden by derived classes. Here is an example:
public class MyDerivedClass : MyBaseClass
{
public override sealed void DoSomething()
{
// Derived class implementation, overrides base implementation.
}
}


Using extern enables you to provide the implementation of a method externally to the project, but this is an advanced topic not covered here.

Defining Properties
Properties are defined in a similar way to fields, but there ’ s more to them. Properties, as already discussed, are more involved than fields in that they can perform additional processing before modifying state — and, indeed, might not modify state at all. They achieve this by possessing two function - like blocks: one for getting the value of the property and one for setting the value of the property.
These blocks, also known as accessors, defined using getand set keywords, respectively, may be used to control the access level of the property. You can omit one or the other of these blocks to create read - only or write - only properties (where omitting the get block gives you write - only access, and omitting the set block gives you read - only access). Of course, that only applies to external code because code elsewhere within the class will have access to the same data that these code blocks have. You can also include accessibility modifiers on accessors — making a getblock public while the set block is protected, for example. You must include at least one of these blocks to obtain a valid property (and, let ’ s face it, a property you can ’ t read or change wouldn ’ t be very useful).
The basic structure of a property consists of the standard access modifying keyword ( public, private, and so on), followed by a type name, the property name, and one or both of the getand setblocks that contain the property processing:
public int MyIntProp
{
get
{
// Property get code.
}
set
{
// Property set code.
}
}


Public properties in .NET are also named using PascalCasing, rather than camelCasing, and, as with fields and methods, PascalCasing is used here.
The first line of the definition is the bit that is very similar to a field definition. The difference is that there is no semicolon at the end of the line; instead, you have a code block containing nested getand setblocks.
get blocks must have a return value of the type of the property. Simple properties are often associated with a single private field controlling access to that field, in which case the get block may return the field ’ s value directly:
// Field used by property.
private int myInt;
// Property.
public int MyIntProp
{
get
{
return myInt;
}
set
{
// Property set code.
}
}


Code external to the class cannot access this myInt field directly due to its accessibility level (it is private). Instead, external code must use the property to access the field. The setfunction assigns a value to the field similarly. Here, you can use the keyword value to refer to the value received from the user of the property:
// Field used by property.
private int myInt;
// Property.
public int MyIntProp
{
get
{
return myInt;
}
set
{
myInt = value;
}
}


value equates to a value of the same type as the property, so if the property uses the same type as the field, then you never have to worry about casting in situations like this.
This simple property does little more than shield direct access to the myInt field. The real power of properties is apparent when you exert a little more control over the proceedings. For example, you might implement your setblock as follows:
set
{
if (value > = 0 & & value < = 10)
myInt = value;
}


Here, you modify myIntonly if the value assigned to the property is between 0 and 10. In situations like this, you have an important design choice to make: What should you do if an invalid value is used? You have four options:
. Do nothing (as in the preceding code).
. Assign a default value to the field.
. Continue as if nothing had gone wrong but log the event for future analysis.
. Throw an exception.

In general, the last two options are preferable. Deciding between them depends on how the class will be used and how much control should be assigned to the users of the class. Exception throwing gives users a fair amount of control and lets them know what is going on so that they can respond appropriately. You can use one of the standard exceptions in the Systemnamespace for this:
set
{
if (value > = 0 & & value < = 10)
myInt = value;
else
throw (new ArgumentOutOfRangeException(“MyIntProp”, value,
“MyIntProp must be assigned a value between 0 and 10.”));
}


This can be handled using try...catch...finally logic in the code that uses the property, as you saw in Chapter 7 .
Logging data, perhaps to a text file, can be useful, such as in production code where problems really shouldn ’ t occur. It enables developers to check on performance and perhaps debug existing code if necessary.
Properties can use the virtual, override, and abstract keywords just like methods, something that isn ’ t possible with fields. Finally, as mentioned earlier, accessors can have their own accessibilities, as shown here:
// Field used by property.
private int myInt;
// Property.
public int MyIntProp
{
get
{
return myInt;
}
protected set
{
myInt = value;
}
}


Here, only code within the class or derived classes can use the setaccessor.
The accessibilities that are permitted for accessors depend on the accessibility of the property, and it is forbidden to make an accessor more accessible than the property to which it belongs. This means that a private property cannot contain any accessibility modifiers for its accessors, whereas public properties can use all modifiers on their accessors. The following Try It Out enables you to experiment with defining and using fields, methods, and properties.

Try It Out : Using Fields, Methods, and Properties
1. Create a new console application called Ch10Ex01 and save it in the directory C:\BegVCSharp\Chapter10.
2. Add a new class called MyClass, using the Add Classshortcut, which will cause the new class to be defined in a new file called MyClass.cs.
3. Modify the code in MyClass.csas follows:
public class MyClass
{
public readonly string Name;
private int intVal;
public int Val
{
get
{
return intVal;
}
set
{
if (value > = 0 & & value < = 10)
intVal = value;
else
throw (new ArgumentOutOfRangeException(“Val”, value,
“Val must be assigned a value between 0 and 10.”));
}
}
public override string ToString()
{
return “Name: “ + Name + “\nVal: “ + Val;
}
private MyClass() : this(“Default Name”)
{
}
public MyClass(string newName)
{
Name = newName;
intVal = 0;
}
}


4. Modify the code in Program.csas follows:
static void Main(string[] args)
{
Console.WriteLine(“Creating object myObj...”);
MyClass myObj = new MyClass(“My Object”);
Console.WriteLine(“myObj created.”);
for (int i = -1; i < = 0; i++)
{
try
{
Console.WriteLine(“\nAttempting to assign {0} to myObj.Val...”, i);
myObj.Val = i;
Console.WriteLine(“Value {0} assigned to myObj.Val.”, myObj.Val);
}
catch (Exception e)
{
Console.WriteLine(“Exception {0} thrown.”, e.GetType().FullName);
Console.WriteLine(“Message:\n\”{0}\””, e.Message);
}
}
Console.WriteLine(“\nOutputting myObj.ToString()...”);
Console.WriteLine(myObj.ToString());
Console.WriteLine(“myObj.ToString() Output.”);
Console.ReadKey();
}


5. Run the application. The result is shown in Figure 10 - 1. Figure 10-1

How It Works

The code in Main()creates and uses an instance of the MyClassclass defined in MyClass.cs. Instantiating this class must be performed using a nondefault constructor because the default constructor of MyClassis private:
private MyClass() : this(“Default Name”)
{
}


Using this( “ Default Name “ ) ensures that Name gets a value if this constructor is ever called, which is possible if this class is used to derive a new class. This is necessary because not assigning a value to the Name field could be a source of errors later.
The nondefault constructor used assigns values to the readonlyfield Name(you can only do this by assignment in the field declaration or in a constructor) and the private field intVal.
Next, Main()attempts two assignments to the Val property of myObj(the instance of MyClass ). A for loop is used to assign the values - 1and 0in two cycles, and a try...catch structure is used to check for any exception thrown. When - 1 is assigned to the property, an exception of type System .ArgumentOutOfRangeException is thrown, and code in the catchblock outputs information about the exception to the console window. In the next loop cycle, the value 0is successfully assigned to the Val property, and through that property to the private intValfield.
Finally, you use the overridden ToString()method to output a formatted string representing the contents of the object:
public override string ToString()
{
return “Name: “ + Name + “\nVal: “ + Val;
}


This method must be declared using the override keyword, because it is overriding the virtual ToString()method of the base System.Object class. The code here uses the property Valdirectly, rather than the private field intVal. There is no reason why you shouldn ’ t use properties from within classes in this way, although there may be a small performance hit (so small that you are unlikely to notice it). Of course, using the property also gives you the validation inherent in property use, which may be beneficial for code within the class as well.

Adding Members from a Class Diagram
The last chapter described how you can use the class diagram to explore the classes in a project. You also learned that the class diagram can be used to add members, and this is what you will look at in this section.
The class diagram is a feature of VS only and is not available in VCE.
All the tools for adding and editing members are shown in the Class Details window in the Class Diagram view. To see this in action, create a class diagram for the MyClass class created in Ch10Ex01. You can see the existing members by expanding the view of the class in the class designer (by clicking on the icon that looks like two downward pointing chevrons). The resulting view is shown in Figure 10 - 2. Figure 10-2

In the Class Details window, you can see the information shown in Figure 10 - 3 when the class is selected. Figure 10-3

The window shows all the currently defined members for the class and includes spaces so you can add new members simply by typing them in.

Adding Methods
To add a method to your class, simply type it in the box labeled < add method > . After you have named a method, you can use the Tab key to navigate to subsequent settings, starting with the return type of the method, and moving on to the accessibility of the method, summary information (which translates to XML documentation, covered in Chapter 31 ), and whether to hide the method in the class diagram.
Once you have added a method, you can expand the entry and add parameters in the same way. For parameters, you also have the option to use the modifiers out, ref, and params. Figure 10 - 4 shows an example of a new method. Figure 10-4

With this new method, the following code is added to your class:
public double MyMethod(double paramX, double paramY)
{
throw new System.NotImplementedException();
}


You can configure other method settings in the Properties window, shown in Figure 10 - 5. Figure 10-5

Among other things, you can make the method static here. Obviously, this technique can ’ t provide the method implementation for you, but it does provide the basic structure, and certainly reduces typing errors!

Adding Properties
Adding properties is achieved in much the same way. Figure 10 - 6 shows a new property added using the Class Details window. Figure 10-6

This adds the property shown here:
public int MyInt
{
get
{
throw new System.NotImplementedException();
}
set
{
}
}


You are left to provide the complete implementation yourself, which includes matching the property with a field for simple properties, removing an accessor if you want the property to be read - or write - only, or applying accessibility modifiers to accessors. However, the basic structure is provided for you.

Adding Fields
Adding fields is just as simple. Simply type the name of the field, choose a type and access modifier, and away you go.

Refactoring Members
One technique that comes in handy when adding properties is the capability to generate a property from a field. This is an example of refactoring, which in general simply means modifying your code using a tool, rather than by hand. This can be accomplished when using a class diagram by right - clicking on a member or simply by right - clicking on a member in code view.
VCE includes limited refactoring capabilities, which unfortunately do not include the field encapsulation described here. VS has many more options than VCE in this area. For example, if the MyClassclass contained the field public string myString; you could right - click on the field and select Refactor Encapsulate Field. That would bring up the dialog shown in Figure 10 - 7. Figure 10-7

Accepting the default options modifies the code for MyClassas follows:
private string myString;
public string MyString
{
get
{
return myString;
}
set
{
myString = value;
}
}


Here, the myStringfield has had its accessibility changed to private, and a public property called MyString has been created and automatically linked to myString. Clearly, reducing the time required to monotonously create properties for fields is a big plus!

Automatic Properties
Properties are the preferred way to access the state of an object because they shield external code from the implementation of data storage within the object. They also give you greater control over how internal data is accessed, as you have seen several times in this chapter’ s code. However, you ’ ll typically define properties in a very standard way — that is, you will have a private member that is accessed directly through a public property. The code for this is almost invariably similar to the code in the previous section, which was autogenerated by the VS refactoring tool.
Refactoring certainly speeds things up when it comes to typing, but C# has another trick up its sleeve: automatic properties. With an automatic property, you declare a property with a simplified syntax and the C# compiler fills in the blanks for you. Specifically, the compiler declares a private field that is used for storage and uses that field in the getand set blocks of your property — without you having to worry about the details.

Use the following code structure to define an automatic property:
public int MyIntProp
{
get;
set;
}


You define the accessibility, type, and name of the property in the usual way, but you don ’ t provide any implementation for the getor setblock. The implementations of these blocks (and the underlying field) is provided by the compiler.
When you use an automatic property you only have access to the data through the property because you will not be able to access the underlying private field without knowing its name, which is defined during compilation. However, that ’ s not really a limitation because using the property name directly is fine. The only limitation of automatic properties is that they must include both a getand setaccessor — you cannot define read - or write - only properties in this way.

Additional Class Member Topics
Now that you ’ ve covered the basics of member definition, it ’ s time to look at some more advanced member topics. This section tackles the following:
. Hiding base class methods.
. Calling overridden or hidden base class methods.
. Nested type definitions .

Hiding Base Class Methods
When you inherit a (non - abstract) member from a base class, you also inherit an implementation. If the inherited member is virtual, then you can override this implementation with the overridekeyword. Regardless of whether the inherited member is virtual, you can, if you want, hide the implementation. This is useful when, for example, a public inherited member doesn ’ t work quite as you want it to.
You can do this simply by using code such as the following:
public class MyBaseClass
{
public void DoSomething()
{
// Base implementation.
}
}
public class MyDerivedClass : MyBaseClass
{
public void DoSomething()
{
// Derived class implementation, hides base implementation.
}
}


Although this code works fine, it generates a warning that you are hiding a base class member. That gives you the chance to correct things if you have accidentally hidden a member that you actually want to use. If you really do want to hide the member, you can use the new keyword to explicitly indicate that this is what you want to do:
public class MyDerivedClass : MyBaseClass
{
new public void DoSomething()
{
// Derived class implementation, hides base implementation.
}
}


This works in exactly the same way but won ’ t show a warning. At this point, it ’ s worthwhile to note the difference between hiding and overriding base class members. Consider the following code:
public class MyBaseClass
{
public virtual void DoSomething()
{
Console.WriteLine(“Base imp”);
}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
Console.WriteLine(“Derived imp”);
}
}


Here, the overriding method replaces the implementation in the base class, such that the following code will use the new version even though it does so through the base class type (using polymorphism):
MyDerivedClass myObj = new MyDerivedClass();
MyBaseClass myBaseObj;
myBaseObj = myObj;
myBaseObj.DoSomething();


This results in the following output:
Derived imp

Alternatively, you could hide the base class method:
public class MyBaseClass
{
public virtual void DoSomething()
{
Console.WriteLine(“Base imp”);
}
}
public class MyDerivedClass : MyBaseClass
{
new public void DoSomething()
{
Console.WriteLine(“Derived imp”);
}
}


The base class method needn ’ t be virtual for this to work, but the effect is exactly the same and the preceding code only requires changes to one line. The result, for a virtual or nonvirtual base class method, is as follows:
Base imp

Although the base implementation is hidden, you still have access to it through the base class.

Calling Overridden or Hidden Base Class Methods
Whether you override or hide a member, you still have access to the base class member from the derived class. There are many situations in which this can be useful, such as the following:
. When you want to hide an inherited public member from users of a derived class but still want access to its functionality from within the class .
. When you want to add to the implementation of an inherited virtual member rather than simply replace it with a new overridden implementation .

To achieve this, you use the base keyword, which refers to the implementation of the base class contained within a derived class (in a similar way to its use in controlling constructors, as shown in the last chapter):
public class MyBaseClass
{
public virtual void DoSomething()
{
// Base implementation.
}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
// Derived class implementation, extends base class implementation.
base.DoSomething();
// More derived class implementation.
}
}


This code executes the version of DoSomethingcontained in MyBaseClass, the base class of MyDerivedClass, from within the version of DoSomethingcontained in MyDerivedClass . As base works using object instances, it is an error to use it from within a static member.

The this Keyword
As well as using base in the last chapter, you also used the this keyword. As with base, thiscan be used from within class members, and, like base, this refers to an object instance, although it is the current object instance (which means you can ’ t use this keyword in static members because static members are not part of an object instance).
The most useful function of the this keyword is the capability to pass a reference to the current object instance to a method, as shown in this example:
public void doSomething()
{
MyTargetClass myObj = new MyTargetClass();
myObj.DoSomethingWith(this);
}


Here, the MyTargetClassthat is instantiated has a method called DoSomethingWith, which takes a single parameter of a type compatible with the class containing the preceding method. This parameter type might be of this class type, a class type from which this class derives, an interface implemented by the class, or (of course) System.Object.

Nested Type Definitions
As well as defining types such as classes in namespaces, you can also define them inside other classes. If you do this, then you can use the full range of accessibility modifiers for the definition, rather than just publicand internal, and you can use the new keyword to hide a type definition inherited from a base class. For example, the following code defining MyClassalso defines a nested class called myNestedClass:
public class MyClass
{
public class myNestedClass
{
public int nestedClassField;
}
}


To instantiate myNestedClass from outside MyClass, you must qualify the name, as shown here:
MyClass.myNestedClass myObj = new MyClass.myNestedClass();

However, you may not be able to do this at all if the nested class is declared as privateor another accessibility level that is incompatible with the code at the point at which this instantiation is performed. The main reason for the existence of this feature is to define classes that are private to the containing class so that no other code in the namespace has access to them.

Interface Implementation
Before moving on, we ’ ll take a closer look at how you go about defining and implementing interfaces. In the last chapter, you learned that interfaces are defined in a similar way as classes, using code such as the following:
interface IMyInterface
{
// Interface members.
}


Interface members are defined like class members except for a few important differences:
. No access modifiers (public, private, protected, or internal) are allowed — all interface members are implicitly public.
. Interface members can ’ t contain code bodies.
. Interfaces can ’ t define field members.
. Interface members can ’ t be defined using the keywords static, virtual, abstract , or sealed.
. Type definition members are forbidden.

You can, however, define members using the new keyword if you want to hide members inherited from base interfaces:
interface IMyBaseInterface
{
void DoSomething();
}
interface IMyDerivedInterface : IMyBaseInterface
{
new void DoSomething();
}


This works in exactly the same way as hiding inherited class members.
Properties defined in interfaces define either or both of the access blocks, getand set, which are permitted for the property, as shown here:
interface IMyInterface
{
int MyInt
{
get;
set;
}
}


Here the int property MyInthas both getand setaccessors. Either of these may be omitted for a property with more restricted access.
This syntax is similar to automatic properties, but remember that automatic properties are defined for classes, not interfaces, and that automatic properties must have both getand setaccessors.
Interfaces do not specify how the property data should be stored. Interfaces cannot specify fields, for example, that might be used to store property data. Finally, interfaces, like classes, may be defined as members of classes (but not as members of other interfaces because interfaces cannot contain type definitions).

Implementing Interfaces in Classes
A class that implements an interface must contain implementations for all members of that interface, which must match the signatures specified (including matching the specified getand setblocks), and
must be public, as shown here:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyClass : IMyInterface
{
public void DoSomething()
{
}
public void DoSomethingElse()
{
}
}


It is possible to implement interface members using the keyword virtualor abstract, but not static or const. Interface members can also be implemented on base classes:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyBaseClass
{
public void DoSomething()
{
}
}
public class MyDerivedClass : MyBaseClass, IMyInterface
{
public void DoSomethingElse()
{
}
}


Inheriting from a base class that implements a given interface means that the interface is implicitly supported by the derived class. Here ’ s an example:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyBaseClass : IMyInterface
{
public virtual void DoSomething()
{
}
public virtual void DoSomethingElse()
{
}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
}
}


Clearly, it is useful to define implementations in base classes as virtual so that derived classes can replace the implementation, rather than hide it. If you were to hide a base class member using the newkeyword, rather than override it in this way, the method IMyInterface.DoSomething would always refer to the base class version even if the derived class were being accessed via the interface.

Explicit Interface Member Implementation
Interface members can also be implemented explicitly by a class. If you do this, the member can only be accessed through the interface, not the class. Implicit members, which you used in the code in the last section, can be accessed either way.
For example, if the class MyClassimplemented the DoSomethingmethod of IMyInterfaceimplicitly, as in the preceding example, then the following code would be valid:
MyClass myObj = new MyClass();
myObj.DoSomething();


This would also be valid:
MyClass myObj = new MyClass();
IMyInterface myInt = myObj;
myInt.DoSomething();


Alternatively, if MyDerivedClassimplements DoSomething explicitly, then only the latter technique is permitted. The code for doing this is as follows:
public class MyClass : IMyInterface
{
void IMyInterface.DoSomething()
{
}
public void DoSomethingElse()
{
}
}


Here, DoSomething is implemented explicitly, and DoSomethingElse implicitly. Only the latter is accessible directly through an object instance of MyClass.

Adding Property Accessors with Nonpublic Accessibility
Earlier it was stated that if you implement an interface with a property, you must implement matching get/set accessors. That isn ’ t strictly true — it is possible to add a get block to a property in a class where the interface defining that property only contains a set block, and vice versa. However, this is only possible if you add the accessor with an accessibility modifier that is more restrictive than the accessibility modifier on the accessor defined in the interface. Because the accessor defined by the interface is, by definition, public, you can only add nonpublic accessors. Here ’ s an example:
public interface IMyInterface
{
int MyIntProperty
{
get;
}
}
public class MyBaseClass : IMyInterface
{
protected int myInt;
public int MyIntProperty
{
get
{
return myInt;
}
protected set
{
myInt = value;
}
}
}


Partial Class Definitions
When you create classes with a lot of members of one type or another, things can get quite confusing, and code files can get very long. One thing that can help, which you ’ ve looked at in earlier chapters, is to use code outlining. By defining regions in code, you can collapse and expand sections to make things easier to read. For example, you might have a class defined as follows:
public class MyClass
{
#region Fields
private int myInt;
#endregion
#region Constructor
public MyClass()
{
myInt = 99;
}
#endregion
#region Properties
public int MyInt
{
get
{
return myInt;
}
set
{
myInt = value;
}
}
#endregion
#region Methods
public void DoSomething()
{
// Do something...
}
#endregion
}


Here, you can expand and contract fields, properties, the constructor, and methods for the class, enabling you to focus only on what you are interested in. It is even possible to nest regions this way, so some regions are only visible when the region that contains them is expanded.
However, if you’re even using this technique, things can still get out of hand. One alternative is to use partial class definitions. Put simply, you use partial class definitions to split the definition of a class across multiple files. You could, for example, put the fields, properties, and constructor in one file and the methods in another. To do that, you just use the partial keyword with the class in each file that contains part of the definition, as follows:
public partial class MyClass
{
...
}


If you use partial class definitions, the partial keyword must appear in this position in every file containing part of the definition.
Partial classes are used to great effect in Windows applications to hide from you the code relating to the layout of forms. You ’ ve already seen this, in fact, in Chapter 2 . A Windows form, in a class called Form1, for example, has code stored in both Form1.csand Form1.Designer.cs. This enables you to concentrate on the functionality of your forms, without worrying about your code being cluttered with information that doesn ’ t really interest you.
One final note about partial classes: Interfaces applied to one partial class part apply to the whole class, meaning that this definition:
public partial class MyClass : IMyInteface1
{
...
}
public partial class MyClass : IMyInteface2
{
...
}


is equivalent to this one:
public class MyClass : IMyInteface1, IMyInteface2
{
...
}


This also applies to attributes, covered in Chapter 27 .

Partial Method Definitions
Partial classes may also define partial methods. Partial methods are defined in one partial class definition without a method body, and implemented in another partial class definition. In both places, the partial keyword is used:
public partial class MyClass
{
partial void MyPartialMethod();
}
public partial class MyClass
{
partial void MyPartialMethod()
{
// Method implementation
}
}


Partial methods can also be static, but they are always private and can ’ t have a return value. Any parameters they use can ’ t be outparameters, although they can be ref parameters. They also can ’ t use the virtual, abstract, override, new, sealed, or externmodifier.
Given these limitations, it is not immediately obvious what purpose partial methods fulfill. In fact, they are important when it comes to code compilation, rather than usage. Consider the following code:
public partial class MyClass
{
partial void DoSomethingElse();
public void DoSomething()
{
Console.WriteLine(“DoSomething() execution started.”);
DoSomethingElse();
Console.WriteLine(“DoSomething() execution finished.”);
}
}
public partial class MyClass
{
partial void DoSomethingElse()
{
Console.WriteLine(“DoSomethingElse() called.”);
}
}


Here, the partial method DoSomethingElseis defined and called in the first partial class definition, and implemented in the second. The output, when DoSomething is called from a console application, is what you might expect:
DoSomething() execution started.
DoSomethingElse() called.
DoSomething() execution finished.


If you were to remove the second partial class definition or partial method implementation entirely (or comment out the code), the output would be as follows:
DoSomething() execution started.
DoSomething() execution finished.


You might assume that what is happening here is that when the call to DoSomethingElseis made, the runtime discovers that the method has no implementation and therefore continues executing the next line of code. What actually happens is a little subtler. When you compile code that contains a partial method definition without an implementation, the compiler actually removes the method entirely. It also removes any calls to the method. When you execute the code, no check is made for an implementation because there is no call to check. This results in a slight — but nevertheless significant — improvement in performance.
As with partial classes, partial methods are useful when it comes to customizing autogenerated or designer- created code. The designer may declare partial methods that you can choose to implement or not depending on the situation. If you don ’ t implement them, you incur no performance hit because effectively the method does not exist in the compiled code.
Consider at this point why partial methods can ’ t have a return type. If you can answer that to your own satisfaction, you can be sure that you fully understand this topic — so that is left as an exercise for you.

Example Application
To illustrate some of the techniques you ’ ve been using so far, in this section you ’ ll develop a class module that you can build on and make use of in subsequent chapters. The class module will contain two classes:
. Card— Representing a standard playing card, with a suit of club, diamond, heart, or spade, and a rank that lies between ace and king.
. Deck— Representing a full deck of 52 cards, with access to cards by position in the deck and the ability to shuffle the deck .

You ’ ll also develop a simple client to ensure that things are working, but you won ’ t use the deck in a full card game application — yet.

Planning the Application
The class library for this application, Ch10CardLib, will contain your classes. Before you get down to any code, though, you should plan the required structure and functionality of your classes.

The Card Class
The Card class is basically a container for two read - only fields: suitand rank. The reason for making the fields read - only is that it doesn ’ t make sense to have a “ blank ” card, and cards shouldn ’ t be able to change once they have been created. To facilitate this, you ’ ll make the default constructor private, and provide an alternative constructor that builds a card from a supplied suit and rank.
Other than that, the Cardclass will override the ToStringmethod of System.Object, so that you can easily obtain a human - readable string representing the card. To make things a little simpler, you ’ ll provide enumerations for the two fields suitand rank.
The Card class is shown in Figure 10 - 8. Figure 10-8

The Deck Class
The Deckclass will maintain 52 Card objects. You can just use a simple array type for this. This array won ’ t be directly accessible because access to the Card object is achieved through a GetCardmethod, which returns the Cardobject with the given index. This class should also expose a Shufflemethod to rearrange the cards in the array. The Deck class is shown in Figure 10 - 9. Figure 10-9

Writing the Class Library
For the purposes of this example, it is assumed that you are familiar enough with the IDE to bypass the standard Try It Out format, so the steps aren ’ t listed explicitly, as they are the same steps you ’ ve used many times. The important thing here is a detailed look at the code. Nonetheless, several pointers are included to ensure that you don ’ t run into any problems along the way.
Both your classes and your enumerations will be contained in a class library project called Ch10CardLib. This project will contain four .csfiles: Card.cs, which contains the Cardclass definition, Deck.cs, which contains the Deckclass definition, and Suit.csand Rank.csfiles containing enumerations.
You can put together a lot of this code using the VS class diagram tool.
Don ’t worry if you are using VCE and don ’t have the class diagram tool at your disposal. Each of the following sections also includes the code generated by the class diagram, so you ’ ll be able to follow along just fine. There are no differences in the code for this project between the IDEs.
To get started, you need to do the following:
1. Create a new class library project called Ch10CardLib and save it in the directory C:\BegVCSharp\Chapter10.
2. Remove Class1.cs from the project.
3. If you are using VS, open the class diagram for the project using the Solution Explorer window (you must have the project selected, rather than the solution, for the class diagram icon to appear). The class diagram should be blank to start with because the project contains no classes.
If you can see the Resources and Settings classes in this view, they can be hidden by right - clicking on them and selecting Remove from Diagram.

Adding the Suit and Rank Enumerations
You can add an enumeration to the class diagram by dragging an Enum from the Toolbox into the diagram, and then filling in the dialog that appears. For example, for the Suitenumeration, fill out the dialog as shown in Figure 10 - 10. Figure 10-10

Next, add the members of the enumeration using the Class Details window. The values required are shown in Figure 10 - 11. Figure 10-11

Add the Rank enumeration from the Toolbox in the same way. The values required for the Rank enumeration are shown in Figure 10 - 12. Figure 10-12

The value entry for the first member is Aceso that the underlying storage of the enum matches the rank of the card, such that Six is stored as 6, for example.
When you ’ ve finished, the diagram should look as shown in Figure 10 - 13. Figure 10-13

The code generated for these two enumerations, in the code files Suit.csand Rank.cs, is as follows:
using System;
using System.Collections.Generic;
using System.Text;
namespace Ch10CardLib
{
public enum Suit
{
Club,
Diamond,
Heart,
Spade,
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Ch10CardLib
{
public enum Rank
{
Ace = 1,
Deuce,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
}
}


If you are using VCE you can add this code manually by adding Suit.csand Rank.cscode files and then entering the code. Note that the extra commas added by the code generator after the last enumeration member do not prevent compilation and do not result in an additional “ empty ” member being created — although they are a little messy.

Adding the Card Class
This section adds the Cardclass, using a mix of the class designer and code editor in VS, or just the code editor in VCE. Adding a class in the class designer is much like adding an enumeration — you drag the appropriate entry from the Toolbox into the diagram. In this case, you drag a Classinto the diagram, and name the new class Card.
Use the Class Details window to add the fields rankand suit, and then use the Properties window to set the Constant Kindof the field to readonly. You also need to add two constructors: a default constructor (private), and one that takes two parameters, newSuitand newRank, of types Suitand Rank, respectively (public). Finally, you override ToString, which requires modifying the Inheritance Modifier in the Properties window to override.
Figure 10 - 14 shows the Class Details window and the Card class with all the information entered. Figure 10-14

Next, modify the code for the class in Card.csas follows (or add the code shown to a new class called Cardin the Ch10CardLib namespace if you are using VCE):
public class Card
{
public readonly Suit suit;
public readonly Rank rank;
public Card(Suit newSuit, Rank newRank)
{
suit = newSuit;
rank = newRank;
}
private Card()
{
}
public override string ToString()
{
return “The “ + rank + “ of “ + suit + “s”;
}
}


The overridden ToString method writes the string representation of the enumeration value stored to the returned string, and the nondefault constructor initializes the values of the suitand rankfields.

Adding the Deck Class
The Deckclass needs the following members defined using the class diagram:
. A private field called cards, of type Card[].
. A public default constructor.
. A public method called GetCard, which takes one intparameter called cardNum and returns an object of type Card.
. A public method called Shuffle, which takes no parameters and returns void.

When these are added, the Class Details for the Deck class will appear as shown in Figure 10 - 15.
Figure 10-15

To make things clearer in the diagram, you can show the relationships among the members and types you have added. In the class diagram, right - click on each of the following in turn, and select Show as Association from the menu:
. cardsin Deck
. suitin Card
. rankin Card

When you have finished, the diagram should look like Figure 10 - 16. Figure 10-16

Next, modify the code in Deck.cs (if you are using VCE, you must add this class first with the code shown here). First you implement the constructor, which simply creates and assigns 52 cards in the cards field. You iterate through all combinations of the two enumerations, using each to create a card. This results in cards initially containing an ordered list of cards:
using System;
using System.Collections.Generic;
using System.Text;
namespace Ch10CardLib
{
public class Deck
{
private Card[] cards;
public Deck()
{
cards = new Card[52];
for (int suitVal = 0; suitVal < 4; suitVal++)
{
for (int rankVal = 1; rankVal < 14; rankVal++)
{
cards[suitVal * 13 + rankVal -1] = new Card((Suit)suitVal,
(Rank)rankVal);
}
}
}


Next, you implement the GetCard method, which either returns the Card object with the requested index or throws an exception as shown earlier:
public Card GetCard(int cardNum)
{
if (cardNum > = 0 & & cardNum < = 51)
return cards[cardNum];
else
throw (new System.ArgumentOutOfRangeException(“cardNum”, cardNum,
“Value must be between 0 and 51.”));
}


Finally, you implement the Shuffle method. This method works by creating a temporary card array and copying cards from the existing cardsarray into this array at random. The main body of this function is a loop that counts from 0 to 51. On each cycle, you generate a random number between 0 and 51, using an instance of the System.Random class from the .NET Framework. Once instantiated, an object of this class generates a random number between 0and X, using the method Next(X). When you have a random number, you simply use that as the index of the Cardobject in your temporary array in which to copy a card from the cardsarray.
To keep a record of assigned cards, you also have an array of boolvariables and assign these to trueas each card is copied. When you are generating random numbers, you check against this array to see whether you have already copied a card to the location in the temporary array specified by the random number. If so, you simply generate another.
This isn ’ t the most efficient way of doing things because many random numbers may be generated before a vacant slot to copy a card into is found. However, it works, it ’ s very simple, and C# code
executes so quickly you will hardly notice a delay.
The code is as follows:
public void Shuffle()
{
Card[] newDeck = new Card[52];
bool[] assigned = new bool[52];
Random sourceGen = new Random();
for (int i = 0; i < 52; i++)
{
int destCard = 0;
bool foundCard = false;
while (foundCard == false)
{
destCard = sourceGen.Next(52);
if (assigned[destCard] == false)
foundCard = true;
}
assigned[destCard] = true;
newDeck[destCard] = cards[i];
}
newDeck.CopyTo(cards, 0);
}
}
}


The last line of this method uses the CopyTomethod of the System.Arrayclass (used whenever you create an array) to copy each of the cards in newDeckback into cards. This means you are using the same set of Cardobjects in the same cards object, rather than creating any new instances. If you had instead used cards= newDeck, then you would be replacing the object instance referred to by cards with another. This could cause problems if code elsewhere were retaining a reference to the original cardsinstance — which wouldn ’ t be shuffled!
That completes the class library code.

A Client Application for the Class Library
To keep things simple, you can add a client console application to the solution containing the class library. To do so, simply right - click on the solution in Solution Explorer and select Add > New Project.
The new project is called Ch10CardClient. To use the class library you have created from this new console application project, add a reference to your Ch10CardLib class library project. Once the console project has been created, you can do this though the Projects tab of the Add Reference dialog, as shown in Figure 10 - 17. Figure 10-17

Select the project, click OK, and the reference is added.
Because this new project was the second one created, you also need to specify that it is the start-up project for the solution, meaning it is the one that will be executed when you click Run. To do this, simply right - click on the project name in the Solution Explorer window and select the Set as StartUp Project menu option.
Next, add the code that uses your new classes. That doesn ’ t require anything particularly special, so the following code will do:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ch10CardLib;
namespace Ch10CardClient
{
class Class1
{
static void Main(string[] args)
{
Deck myDeck = new Deck();
myDeck.Shuffle();
for (int i = 0; i < 52; i++)
{
Card tempCard = myDeck.GetCard(i);
Console.Write(tempCard.ToString());
if (i != 51)
Console.Write(“, “);
else
Console.WriteLine();
}
Console.ReadKey();
}
}
}


The result is shown in Figure 10 - 18. Figure 10-18

This is a random arrangement of the 52 playing cards in the deck. You ’ ll continue to develop and use this class library in later chapters.

Summary
This chapter completed the discussion of how to define basic classes. There ’ s still plenty to cover, but the techniques covered so far enable you to create quite complicated applications already.
You looked at how to define fields, methods, and properties, including the various access levels and modifier keywords. You also looked at the tools you can use to get the outline of a class together in half the time.
You explored inheritance behavior in detail, learning how to hide unwanted inherited members with the new keyword, and extending base class members rather than replacing their implementation, using the base keyword. You also looked at nested class definitions, had a detailed look at interface definition and implementation, including the concepts of explicit and implicit implementation, and learned how to split definitions between code files using partial class and method definitions.
Finally, you developed and used a simple class library representing a deck of playing cards, making use of the handy class diagram tool to make things easier. You ’ ll make further use of this library in later chapters.
This chapter covered the following main topics:
. How to define fields, methods, and properties .
. Tools available for creating the outline of a class .
. More about inheritance behavior.
. Interface definition and implementation.
. Partial classes and method definitions.
. Creating and deploying a simple class library.

In the next chapter, you look at collections, a type of class you will frequently use in your development.

Exercises
1. Write code that defines a base class, MyClass, with the virtual method GetString . This method should return the string stored in the protected field myString, accessible through the write - only public property ContainedString.
2. Derive a class, MyDerivedClass, from MyClass. Override the GetString method to return the string from the base class, using the base implementation of the method, but add the text “ (output from derived class) ” to the returned string.
3. Partial method definitions must use the void return type. Provide a reason why this might be so.
4. Write a class called MyCopyableClass that is capable of returning a copy of itself using the method GetCopy. This method should use the MemberwiseClone method inherited from System.Object. Add a simple property to the class, and write client code that uses the class to confirm that everything is working.
5. Write a console client for the Ch10CardLib library that draws five cards at one time from a shuffled Deck object. If all five cards are the same suit, then the client should display the card names onscreen along with the text Flush!; otherwise, it should quit after 50 cards with the text No flush.

Post a Comment

About This Blog

  © Blogger template Simple n' Sweet by Ourblogtemplates.com 2009

Back to TOP