
C# ПІДРУЧНИКИ / c# / Apress - Accelerated C# 2005
.pdf
374 C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S
The .NET Framework offers yet another type of conversion mechanism, which works via the System.ComponentModel.TypeConverter. It is another converter that is external to the class of the object instance that needs to be converted, such as System.Convert. The advantage of using TypeConverter is that you can use it at design time within the IDE as well as at run time. You create your own special TypeConverter for your class that derives from TypeConverter, then you associate your new type converter to your class via the TypeConverterAttribute. At design time, the IDE can examine the metadata for your type and, from the information gleaned from the metadata, create an instance of your type’s converter. That way, it can convert your type to and from representations that it sees fit to use. I won’t go into the detail of creating a TypeConverter derivative, but if you’d like more information, look up the “Generalized Type Conversion” topic in the MSDN.
Prefer Type Safety at All Times
You already know that C# is a strongly-typed language. A strongly-typed language and its compiler form a dynamic duo capable of sniffing out bugs before they strike. Even though every object in the managed world derives from System.Object, it’s a bad idea to treat every object generically via a System.Object reference. One reason is efficiency; for example, if you were to maintain a collection of Employee objects via references to System.Object, you would always have to cast instances of them to type Employee before you can call the Evaluate method on them. Although this inefficiency is slight when reference types are used and the cost succeeds, this efficiency problem is amplified by magnitudes with value types, since unnecessary boxing operations are generated in the IL code. I’ll cover the boxing inefficiencies in the following sections dealing with value types. The biggest
problem with all of this casting when using reference types is when the cast fails and an exception is thrown. By using strong types, you can catch these problems and deal with them at compile time.
Another prominent reason to prefer strong type usage is associated with catching errors. Consider the case when implementing interfaces such as ICloneable. Notice that the Clone method returns an instance as type Object. Clearly, this is done so that the interface will work generically across all types. However, it can come at a price.
C++ and C# are both strongly typed languages. Every variable is declared with a type. Along with this comes type safety, which the compiler supplies to help you avoid errors. For example, it keeps you from assigning an instance of class Apple from an instance of class MonkeyWrench. However, C# (and C++) allows you to work in a less type-safe way. You can reference every object through the type Object; however, doing so throws away the type safety, and the compiler will allow you to assign an instance of type Apple from an instance of type MonkeyWrench as long as both references are of type Object. Unfortunately, even though the code will compile, you run the risk of generating a runtime error once the CLR realizes what sort of craziness you’re attempting to do. So, the more you utililize the type safety of the compiler, the more error detection it can do at compile time, and catching errors at compile time is always more desirable than catching errors at run time.
Let’s have a closer look at the efficiency facet of the problem. Treating objects generically can impose a runtime inefficiency when you need to downcast to the actual type. In reality, this efficiency hit is very minor with managed reference types in C#, unless you’re doing it many times within a loop.
In some situations, the C# compiler will generate much more efficient code if you provide a type-safe implementation of a well-defined method. Consider this typical foreach statement in C#:
foreach( Employee emp in collection ) { // Do Something
}
Quite simply, the code loops over all the items in collection. Within the body of the foreach statement, a variable emp of type Employee references the current item in the collection during iteration. One of the rules enforced by the C# compiler for the collection is that it must implement a public method named GetEnumerator, which returns a type used to enumerate the items in the collection. This method is implemented as a result of the collection type implementing the IEnumerable

C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S |
375 |
interface and typically returns a forward iterator on the collection of contained objects.6 One of the rules on the enumerator type is that it must implement a public property named Current, which allows access to the current element. This property is part of the IEnumerator interface; however, notice that IEnumerator.Current is typed as System.Object. This leads to another rule with regards to the foreach statement. It states that the object type of IEnumerator.Current, the real object type, must be explicitly castable to the type of the iterator in the foreach statement, which in this example is type Employee. If your collection’s enumerator types its Current property as System.Object, the compiler must always perform the cast to type Employee. However, you can see that the compiler can generate much more efficient code if your Current property on your enumerator is typed as Employee.
So, what can you do to remedy this situation in the C# world? Basically, whenever you implement an interface that contains methods with essentially nontyped return values, consider using explicit interface implementation to hide the method from the public interface of the class, while implementing a more type-safe version as part of the public interface of the class. Let’s look at an example using the IEnumerator interface:
using System;
using System.Collections;
public class Employee
{
public void Evaluate() {
Console.WriteLine( "Evaluating Employee..." );
}
}
public class WorkForceEnumerator : IEnumerator
{
public WorkForceEnumerator( ArrayList employees ) { this.enumerator = employees.GetEnumerator();
}
public Employee Current { get {
return (Employee) enumerator.Current;
}
}
object IEnumerator.Current { get {
return enumerator.Current;
}
}
public bool MoveNext() {
return enumerator.MoveNext();
}
public void Reset() { enumerator.Reset();
}
6.I use the word “typically” here since the iterators could be reverse iterators. In Chapter 9, I show how you can easily create reverse and bidirectional iterators that implement IEnumerator.

376 C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S
private IEnumerator enumerator;
}
public class WorkForce : IEnumerable
{
public WorkForce() {
employees = new ArrayList();
// Let's put an employee in here for demo purposes. employees.Add( new Employee() );
}
public WorkForceEnumerator GetEnumerator() { return new WorkForceEnumerator( employees );
}
IEnumerator IEnumerable.GetEnumerator() { return new WorkForceEnumerator( employees );
}
private ArrayList employees;
}
public class EntryPoint
{
static void Main() {
WorkForce staff = new WorkForce(); foreach( Employee emp in staff ) {
emp.Evaluate();
}
}
}
Look carefully at the example and notice how the typeless versions of the interface methods are implemented explicitly. Remember that in order to access those methods, you must first cast the instance to the interface type. However, the compiler doesn’t do that when it generates the foreach loop. Instead, it simply looks for methods that match the rules already mentioned. So, it will find the strongly typed versions and use them. I encourage you to step through the code using a debugger to see it in action. In fact, these types aren’t even required to implement the interfaces that they implement—namely, IEnumerable and IEnumerator. You can comment the interface names out and simply implement the methods that match the signatures of the ones in the interfaces. Also, you can make this code considerably more efficient by using generics, which I covered in Chapter 10.
Let’s take a closer look at the foreach loop generated by the compiler to get a better idea of what sorts of efficiency gains you get. In the following code, I’ve removed the strongly typed versions of the interface methods, and as expected, the example runs pretty much the same as before from an outside perspective:
using System;
using System.Collections;
public class Employee
{
public void Evaluate() {
Console.WriteLine( "Evaluating Employee..." );
}
}

C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S |
377 |
public class WorkForceEnumerator : IEnumerator
{
public WorkForceEnumerator( ArrayList employees ) { this.enumerator = employees.GetEnumerator();
}
public object Current { get {
return enumerator.Current;
}
}
public bool MoveNext() {
return enumerator.MoveNext();
}
public void Reset() { enumerator.Reset();
}
private IEnumerator enumerator;
}
public class WorkForce : IEnumerable
{
public WorkForce() {
employees = new ArrayList();
// Let's put an employee in here for demo purposes. employees.Add( new Employee() );
}
public IEnumerator GetEnumerator() {
return new WorkForceEnumerator( employees );
}
private ArrayList employees;
}
public class EntryPoint
{
static void Main() {
WorkForce staff = new WorkForce(); foreach( Employee emp in staff ) {
emp.Evaluate();
}
}
}
Of course, the generated IL is not as efficient. To see the efficiency gains within the foreach loop, you must load the compiled versions of each example into ILDASM and open up the IL code for the Main method. You’ll see that the weakly typed example has extra castclass instructions in it that are not present in the strongly typed example. On my development machine, I ran the foreach loop 20,000,000 times in a tight loop to create a crude benchmark. The typed version of the enumerator was 15% faster than the untyped version. That’s a considerable gain if you’re working on the game loop in the next-best-selling Managed DirectX game.

378 C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S
Using Immutable Reference Types
When creating a well-designed contract or interface, you should always consider the mutability or immutability of types declared in the contract. For example, if you have a method that accepts a parameter, you should consider whether it is valid for the method to modify the parameter. Suppose you want to ensure that the method body cannot modify a parameter. If the parameter is a value type that is passed without the ref keyword, then the method receives a copy of the parameter, and you’re guaranteed that the source value is not modified. However, for reference types, it’s much more complicated, since only the reference is copied rather than the object the reference points to.
■Note If you come from a C++ background, you’ll recognize that immutability is implemented via the const keyword. To follow this technique is to be const-correct. Even though C++ may seem superior to those who are upset that C# doesn’t support const, keep in mind that in C++, you can cast away the const-ness using const_cast. Therefore, an immutable implementation is actually superior to the C++ const keyword, since you can’t simply cast it away.
A great example of an immutable class within the Standard Library is System.String. Once you create a String object, you can’t ever change it. There’s no way around it; that’s the way the class is designed. You can create copies, and those copies can be modified forms of the original, but you simply cannot change the original instance for as long as it lives, without resorting to unsafe code. If you understand that, you’re probably starting to get the gist of where I’m going here: For a referencebased object to be passed into a method, such that the client can be guaranteed that it won’t change during the method call, it must itself be immutable.
In a world such as the CLR where objects are held by reference by default, this notion of immutability becomes very important. Let’s suppose that System.String was mutable, and let’s suppose you could write a method such as the following fictitious method:
public void PrintString( string theString )
{
//Assuming following line does not create a new
//instance of String but modifies theString theString += ": there, I printed it!"; Console.WriteLine( theString );
}
Imagine the callers’ dismay when they get further along in the code that called this method and now their string has this extra stuff appended onto the end of it. That’s what could happen if System. String were mutable. You can see that String’s immutability exists for a reason, and maybe you should consider adding the same capability to your design.
There are many ways to solve the C# const parameter problem for objects that must be mutable. One general solution is to create two classes for each mutable class you create if you’ll ever want your clients to be able to pass a const version of the object to a parameter. As an example, let’s revisit the previous ComplexNumber class. If implemented as an object rather than a value type, ComplexNumber is a perfect candidate to be an immutable type, similar to String. In such cases, an operation such as ComplexNumber.Add() would need to produce a new instance of ComplexNumber rather than modify the object referenced by this. But for the sake of argument, let’s consider what you would want to do if ComplexNumber were allowed to be mutable. You could allow access to the real and imaginary fields via read-write properties. But how would you be able to pass the object to a method and be guaranteed that the method won’t change it by accessing the setter of the one of the properties? One answer, as in many other object-oriented designs, is the technique of introducing another class. Consider the following code:

C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S |
379 |
using System;
public sealed class ComplexNumber
{
public ComplexNumber( double real, double imaginary ) { this.real = real;
this.imaginary = imaginary;
}
public double Real { get {
return real;
}
set {
real = value;
}
}
public double Imaginary { get {
return imaginary;
}
set {
imaginary = value;
}
}
// Other methods removed for clarity
private double real; private double imaginary;
}
public sealed class ConstComplexNumber
{
public ConstComplexNumber( ComplexNumber pimpl ) { this.pimpl = pimpl;
}
public double Real { get {
return pimpl.Real;
}
}
public double Imaginary { get {
return pimpl.Imaginary;
}
}

380 C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S
private readonly ComplexNumber pimpl7;
}
public sealed class EntryPoint
{
static void Main() {
ComplexNumber someNumber = new ComplexNumber( 1, 2 ); SomeMethod( new ConstComplexNumber(someNumber) );
//We are guaranteed by the contract of ConstComplexNumber that
//someNumber has not been changed at this point.
}
static void SomeMethod( ConstComplexNumber number ) { Console.WriteLine( "( {0}, {1} )",
number.Real, number.Imaginary );
}
}
Notice that I’ve introduced a shim class named ConstComplexNumber. When a method wants to accept a ComplexNumber object but guarantee that it won’t change that parameter, then it accepts a ConstComplexNumber rather than a ComplexNumber. Of course, for the case of ComplexNumber, the best solution would have been to implement it as an immutable type in the first place.8 But, you can easily imagine a class much more complex than ComplexNumber (no pun intended . . . really!) that may require a technique similar to this to guarantee that a method won’t modify an instance of it.
As with many problems in software design, you can achieve the same goal in many ways. This shim technique isn’t the only way to solve this problem. You could also achieve the same goal with interfaces. You could define one interface that declares all of the methods that modify the object— say, IModifyableComplexNumber—and another interface that declares methods that don’t modify the object—say, IConstantComplexNumber. Then, you could create a third interface, IComplexNumber, which derives from both of these, and, finally, ComplexNumber would then implement the IComplexNumber interface. For methods that must take the parameter as immutable, you can simply pass the instance as the IConstantComplexNumber type.
Before you write these techniques off as academic exercises, please take time to consider and understand the power of immutability in robust software designs. So many articles on const-correctness exist in the C++ community for good reason. And there is no good reason that you shouldn’t apply these same techniques to your C# designs.
Value Type Canonical Form
While investigating the notions of canonical forms for value types, you’ll find that some of the concepts that apply to reference types may be applied here as well. However, there are many notable differences. For example, it makes no sense to implement ICloneable on a value type. Technically, you could, but since ICloneable returns an instance of type Object, your value type’s implementa-
7.For those of you curious about the curious name of this field, read about the Pimpl Idiom in Herb Sutter’s
Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Exception-Safety Solutions (Boston, MA: Addison-Wesley Professional, 1999).
8.To avoid this complex ball of yarn, many of the value types defined by the .NET Framework are, in fact, immutable.

C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S |
381 |
tion of ICloneable.Clone() would most likely just be returning a boxed copy of itself. You can get the exact same behavior by simply casting a value type instance into a reference to System.Object, as long as your value type doesn’t contain any reference types. In fact, you could argue that value types that contain mutable reference types are bordering on poor design. Value types are best used for immutable, lightweight data chunks. So, as long as the reference types your value type does contain are immutable—similar to System.String, for example—you don’t have to worry about implementing ICloneable on your value type. If you find yourself being forced to implement ICloneable on your value type, take a closer look at the design. It’s possible that your value type should be a reference type.
Value types don’t need a finalizer, and, in fact, C# won’t let you create a finalizer via the destructor syntax on a struct. Similarly, value types have no need to implement the IDisposable interface, unless they contain objects by reference, which implement IDisposable, or if they hold onto scarce system resources. In those cases, it’s important that value types implement IDisposable. In fact, you can use the using statement with value types that implement IDisposable.
■Tip Since value types cannot implement finalizers, they cannot guarantee that the cleanup code in Dispose() executes even if the user forgets to call it explicitly. Therefore, declaring fields of reference type within value types should be discouraged. If that field is a value type that requires disposal, you cannot guarantee that disposal happens.
Value types and reference types do share many implementation idioms. For example, it makes sense for both to consider implementing IComparable, IFormattable, and possibly IConvertible.
In the rest of this section, I’ll cover the different canonical concepts that you should apply while designing value types. Specifically, you’ll want to override Equals() for greater runtime efficiency, and you’ll want to be cognizant of what it means for a value type to implement an interface. Let’s get started.
Override Equals() for Better Performance
You’ve already seen the main differences between the two types of equivalence in the CLR and in C#. For example, you now know that reference types (class instances) define equality as an identity test by default, and value types (struct instances) use value equality as an equivalence test. Reference types get their default implementation from Object.Equals(), whereas value types get their default implementation from System.ValueType’s override of Equals(). All struct types implicitly derive from System.ValueType.
You should implement your own override of Equals() for each struct that you define. You can compare the fields of your object more efficiently, since you know their types and what they are at compile time. Let’s update the ComplexNumber example from previous sections, converting it to a struct and implementing a custom Equals() override:
using System;
public struct ComplexNumber : IComparable
{
public ComplexNumber( double real, double imaginary ) { this.real = real;
this.imaginary = imaginary;
}
public override bool Equals( object other ) { bool result = false;
if( other is ComplexNumber ) {
ComplexNumber that = (ComplexNumber) other ;

382 C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S
result = InternalEquals( that );
}
return result;
}
public override int GetHashCode() { return (int) this.Magnitude;
}
public static bool operator ==( ComplexNumber num1, ComplexNumber num2 ) {
return num1.Equals(num2);
}
public static bool operator !=( ComplexNumber num1, ComplexNumber num2 ) {
return !num1.Equals(num2);
}
public int CompareTo( object other ) { if( !(other is ComplexNumber) ) {
throw new ArgumentException( "Bad Comparison!" );
}
ComplexNumber that = (ComplexNumber) other;
int result;
if( InternalEquals(that) ) { result = 0;
}else if( this.Magnitude > that.Magnitude ) { result = 1;
}else {
result = -1;
}
return result;
}
private bool InternalEquals( ComplexNumber that ) { return (this.real == that.real) &&
(this.imaginary == that.imaginary);
}
public double Magnitude { get {
return Math.Sqrt( Math.Pow(this.real, 2) + Math.Pow(this.imaginary, 2) );
}
}
// Other methods removed for clarity
private readonly double real; private readonly double imaginary;
}

C H A P T E R 1 3 ■ I N S E A R C H O F C # C A N O N I C A L F O R M S |
383 |
public sealed class EntryPoint
{
static void Main()
{
ComplexNumber num1 = new ComplexNumber( 1, 2 ); ComplexNumber num2 = new ComplexNumber( 1, 2 );
bool result = num1.Equals( num2 );
}
}
From looking at the example code, you can see that the code has only minimal changes compared to the reference type version. The type is now declared as a struct rather than a class, and notice that it also still supports IComparable. I’ll have more to say about structs implementing interfaces later in the section titled “Do Values of This Type Support Any Interfaces?” The keen reader may notice that the efficiency still stands to improve by a fair amount. The trick lies in the concept of boxing and unboxing. Remember, any time a value type instance is passed as an object in a method parameter list, it must be implicitly boxed if it is not boxed already. That means that when the Main method calls the Equals method, it must first box the num2 value. What’s worse is that the method will typically unbox the value in order to use it. Thus, in the process of comparing two values for equality, you’ve made two more copies of one of them.
To solve this problem, you can define two overloads of Equals(). You want a type-safe version that takes a ComplexNumber as its parameter type, and you still need to override the Object.Equals method as before.
■Note The .NET 2.0 Framework formalizes this concept with the generic interface IEquatable<T>, which declares one method that is the type-safe version of Equals().
Let’s take a look at how the code changes:
using System;
public struct ComplexNumber : IComparable,
IComparable<ComplexNumber>,
IEquatable<ComplexNumber>
{
public ComplexNumber( double real, double imaginary ) { this.real = real;
this.imaginary = imaginary;
}
public bool Equals( ComplexNumber other ) { return (this.real == other.real) &&
(this.imaginary == other.imaginary);
}
public override bool Equals( object other ) { bool result = false;
if( other is ComplexNumber ) {
ComplexNumber that = (ComplexNumber) other ;
result = Equals( that );
}