Effective C# - Part I

Topics

Language Elements

1. Always Use Properties Instead of Accessible Data Members

Properties let you expose data members as part of your public interface and still provide the encapsulation you want in an object-oriented environment. Client code accesses properties as though they are accessing public variables.

The .NET Framework assumes that you'll use properties for your public data members. In fact, the data binding code classes in the .NET Framework, especially WPF, support properties, not public data members. Data binding ties a property of an object to a user interface control. But that doesn't mean properties should be used exclusively in UI logic. You should still be using properties for other classes and structures. Properties are far easier to change as you discover new requirements or behaviors over time. See class Person below.

With properties, adding multithreaded support is easy. Simply enhance the implementation of the get and set methods to provide synchronized access to the data. See class PersonThreadSafe below.

Properties have all the language features of methods. Properties can be virtual, abstract, and also part of an interface definition.

namespace Item1NS

{

    // Properties are far easier to change as you add or improve behavior

    class Person

    {

        private string _FirstName;

        public string FirstName

        {

            get { return _FirstName; }

            set

            {

                if (string.IsNullOrEmpty(value))

                    throw new ArgumentException("FirstName cannot be null or empty");

                else

                    _FirstName = value;

            }

        }

    }

 

    // Adding multi-threading support to properties is also easy

    class PersonThreadSafe

    {

        private string _FirstName;

        private object _lock = new object();

        public string FirstName

        {

            get { return _FirstName; }

            set

            {

                if (string.IsNullOrEmpty(value))

                    throw new ArgumentException("FirstName cannot be null or empty");

                else

                    lock (_lock)

                    {

                        _FirstName = value;

                    }

            }

        }

    } 

}

2. Prefer readonly to const

C# has two different versions of constants: compile-time constants and runtime constants. Prefer runtime constants over compile-time constants. Compile-time constants are slightly faster, but far less flexible, than runtime constants. Reserve the compile-time constants for when performance is critical and the value of the constant will never change over time.

You declare runtime constants with the readonly keyword. Compile-time constants are declared with the const keyword:

public const int _Millennium = 2000;             // Compile time constant
public static readonly int _ThisYear = 2004;     // Runtime constant

The differences in the behavior of compile-time and runtime constants follow from how they are accessed. A compile-time constant is replaced with the value of that constant in your object code:

if ( myDateTime.Year == _Millennium ) // Compiles to: if ( myDateTime.Year == 2000 )

Runtime constants are evaluated at runtime. The IL generated when you reference a read-only  constant references the readonly variable, not the value.

This distinction places several restrictions on when you are allowed to use either type of constant. Compile-time constants can be used only for primitive types (built-in integral and floating-point types), enums, or strings. These primitive types are the only ones that can be replaced with literal values in the compiler-generated IL. The following construct does not compile. You cannot initialize a compile-time constant using the new operator, even when the type being initialized is a value type:

//Does not compile, use readonly instead:
private const DateTime _classCreation = new DateTime( 2000, 1, 1, 0, 0, 0 );

readonly values are also constants, in that they cannot be modified after the constructor has executed. But read-only values are different, in that they are assigned at runtime. You have much more flexibility in working with runtime constants. For one thing, runtime constants can be any type. You must initialize them in a constructor, or you can use an initializer. You can make readonly values of the DateTime structures; you cannot create DateTime values with const.

The most important distinction is that readonly values are resolved at runtime. The IL generated when you reference a readonly constant references the readonly variable, not the value. This difference has far-reaching implications on maintenance over time. Compile-time constants generate the same IL as though you've used the numeric constants in your code, even across assemblies: A constant in one assembly is still replaced with the value when used in another assembly. Consider the following code in assembly A:

class Constants
{
    public static readonly int START = 10;
    public const int END = 20;
}


In another assembly, assembly B, you have code that writes the value of both constants:


Trace.Writeline( START);
Trace.Writeline( END );

Once all assemblies are compiled, the above trace statements give obvious outputs: 10 and 20. Time passes and you release a new version of assembly A with the following changes

class Constants
{
    public static readonly int START = 100;
    public const int END = 200;
}

You release assembly A without rebuilding assembly B. You'd expect that the trace lines would give 100 and 200. Wrong! Trace outputs 100 and 20!

The C# compiler placed the const value of 20 into the Application assembly instead of a reference to the storage used by END. Contrast that with the START value. It was declared as readonly: It gets resolved at runtime. Therefore, the application assembly makes use of the new value without even recompiling the application assembly; simply installing an updated version of the A assembly is enough to change the behavior of all clients using that value. Updating the value of a public constant should be viewed as an interface change. You must recompile all code that references that constant. Updating the value of a read-only constant is an implementation change; it is binary compatible with existing client code.

Finally, not that known constant values (i.e., const) can generate slightly more efficient code than the variable accesses necessary for readonly values. However, any gains are slight and should be weighed against the decreased flexibility. Be sure to profile performance differences before giving up the flexibility.

class Item2

{

    public const int _Millennium = 2000;        // Compile time constant

    public readonly double PI = 3.14;           // Runtime constant

 

    // The following does not compile

    // Error CS0283: The type 'System.DateTime' cannot be declared const

    //private const DateTime START_YEAR = new DateTime(2009, 1, 1);

 

    // The following compiles. Uses 'readonly' rather than 'const'

    public static readonly DateTime START_YEAR = new DateTime(2009,1,1); 

}

3. Prefer the is or as Operators to Casts

Many times in C#, you write functions that take System.Object parameters because the framework defines the method signature for you. You likely need to attempt to downcast those objects to other types, either classes or interfaces. You've got two choices: Use the as operator or use that old C standby, the cast. You also have a defensive variant: You can test a conversion with is and then use as or casts to convert it.

The correct choice is to use the as operator whenever you can because it is safer than blindly casting and is more efficient at runtime. The is and as operators do not perform any user-defined conversions. They succeed only if the runtime type matches the sought type; they never construct a new object to satisfy a request . See method TestISAndAsVsCast1() below.

The biggest difference between the as operator and the cast operator is how user-defined conversions are treated. The as and is operators examine the runtime type of the object being converted; they do not perform any other operations. If a particular object is not the requested type or is derived from the requested type, they fail. Casts, on the other hand, can use conversion operators to convert an object to the requested type. This includes any built-in numeric conversions. Casting a long to a short can lose information. See method TestISAndAsVsCast2() below.

Recall that the as operator does not work on value types. You're stuck with a cast. But you're not necessarily stuck with the behaviors of casts. You can use the is statement to remove the chance of exceptions or conversions

int i = 0;
object o = GetSomeObject();
if (o is int)
    i = (int)o;         // Use cast after checking that o is an int

The is operator should be used only when you cannot convert the type using as. Otherwise, it's simply redundant. See TestIsAndAs() method below.

class Item3

{

    /// <summary>

    /// Illustrates general concept of using is/as vs. cast

    /// </summary>

    public void TestISAndAsVsCast1()

    {

        // Get an object instance

        object o = GetSomeObject();

 

        // Approach 1. Correct           

        DateTime? dt = o as DateTime?;

        if (dt != null)

            Trace.WriteLine(dt.ToString());

 

        // Approach 2. Messy

        // With casts you must check null as null can be converted to any reference type using a cast

        // You must also catch exceptions.

        try

        {

            DateTime? dt2 = (DateTime?)o;

            if (dt2 != null)

            {

                Trace.WriteLine(dt2.ToString());

            }

            else

            {

                // Report null ref exception

            }

        }

        catch (Exception ex)

        {

            Trace.WriteLine(ex.Message);

        }

    }

 

    // Test method whose return parameter is System.Object

    private object GetSomeObject()

    {

        Nullable<DateTime> dt = DateTime.Now;

        return dt;

    }

 

    /// <summary>

    /// Illustrates that cast can invoke implicit conversions.

    /// Remember that user-defined conversion operators operate only on the compile-time type of an

    /// object, not on the runtime type.

    /// </summary>

    public void TestISAndAsVsCast2()

    {

        // Get an object instance

        Person p = new Person();

 

        // Invokes user-defined conversion operator!

        try

        {

            Student s = (Student)p;

            if (s != null)

            {

                Trace.WriteLine(s.StudentID);

            }

            else

            {

                // Report null ref exception

            }

        }

        catch (Exception ex)

        {

            Trace.WriteLine(ex.Message);

        }

    }

 

    /// <summary>

    /// The is operator should be used only when you cannot convert the type using as.

    /// Otherwise, it's simply redundant

    /// </summary>

    public void TestIsAndAs()

    {

        object o = GetSomeObject();

        DateTime? dt = null;

 

        // Redundant approach 1

        {

            if (o is DateTime?)

                dt = o as DateTime?;

        }

 

        // Redundant approach 2

        {

            if ((o as DateTime?) != null)

                dt = o as DateTime?;

        }

 

        // Proper approach

        dt = o as DateTime?;

        if (dt != null)

        {

            // Required code ....

        }

    }

}

 

namespace Item3NS

{

    // Contains an implicit conversion operator that converts from SomeType to DateTime

    public class Person

    {

        public string Name { get; set; }

        public static implicit operator Student(Person p)

        {

            return new Student();

        }

    }

 

    public class Student

    {

        public string StudentID { get; set; }

    }

}

4. Use Conditional Attributes Instead of #if

#if / #endif blocks have been used to produce different builds from the same source, most often debug and release variants. #if / #endif blocks are too easily abused, creating code that is hard to understand and harder to debug.

C# has the [Conditional] attribute to indicate whether a method should be called based on an environment setting. It's a cleaner way to describe conditional compilation than #if / #endif. The [Conditional] attribute is applied at the method level, so it forces you to separate conditional code into distinct methods. Therefore, use the [Conditional] attribute instead of  #if / #endif block when you create conditional code blocks. See ShowFrames_Old() and ShowFrames_New() methods below.

The [Conditional] attribute can be applied only to entire methods. In addition, any method with a [Conditional] attribute must have a return type of void. You cannot use the [Conditional] attribute for blocks of code inside methods or with methods that return values.

Note that the [Conditional] attribute does not affect the code generated for the attributed function, ShowFrames_New() function, rather, it modifies the calls to the function. If the DEBUG symbol is defined, you get this:

public void TestConditional()
{
    ShowFrames_New();
    Trace.WriteLine("This is a test");
}


If not, you get this:

public void TestConditional()
{
    Trace.WriteLine("This is a test");
}

The body of the ShowFrames_New() function is the same, regardless of the state of the environment variable. Whether the DEBUG environment variable is defined or not, the ShowFrames_New() method is compiled and delivered with the assembly. That might seem inefficient, but the only cost is disk space. The ShowFrames_New() function does not get loaded into memory and JITed unless it is called. Its presence in the assembly file is immaterial.

Note that you can also create methods that depend on more than one environment variable. When you apply multiple conditional attributes, they are OR-combined. For example, this version of ShowFrames_New() would be called when either DEBUG or TRACE is true:

[ Conditional( "DEBUG" ), Conditional( "TRACE" ) ]
private void ShowFrames_New( )

class Item4

{

    public void TestConditional()

    {

        ShowFrames_New();

 

        Trace.WriteLine("This is a test");

 

    }

    /// <summary>

    /// Using the #if and #endif pragmas, you've created an empty method in your release builds.

    /// The ShowFrames_Old() method gets called in all builds, release and debug. It doesn't

    /// do anything in the release builds, but you pay for the method call. You also pay a small

    /// cost to load and JIT the empty routine.

    /// </summary>

    private void ShowFrames_Old()

    {

        #if DEBUG

        StackFrame[] frames = new StackTrace(1).GetFrames();

        foreach (StackFrame frame in frames)

            Trace.WriteLine(frame.GetMethod().Name);

        #endif

    }

 

    /// <summary>

    /// Using the Conditional attribute, you can isolate functions that should be part of your

    /// classes only when a particular environment variable is defined or set to a certain value.

    /// The most common use of this feature is to instrument your code with debugging statements.

    /// </summary>

    [Conditional("DEBUG")]

    private void ShowFrames_New()

    {

        StackFrame[] frames = new StackTrace(1).GetFrames();

        foreach (StackFrame frame in frames)

            Trace.WriteLine(frame.GetMethod().Name);

    }

 

}

5. Always Override ToString()

System.Object.ToString() is one of the most used methods in the .NET environment. The System.Object version returns the name of the type, which in most cases can be useless. You should write a reasonable version for all the clients of your class. Otherwise, you force every user of your class to use the properties in your class and create a reasonable human-readable representation. This string representation of your type can be used to easily display information about an object to users. In fact, this the string representation of objects is very useful for debugging. When you create more complicated types, you should implement the more sophisticated IFormattable.ToString(). IFormattable is the interface you use when you need to create different forms of string output.

See class Person and method TestToStringOverride() below.

Note that any implementation of IFormattable.ToString() is specific to the type, but you must handle certain cases whenever you implement the IFormattable interface. First, you must support the general format, "G". Second, you must support the empty format in both variations: "" and null. All three format specifiers must return the same string as your override of the Object.ToString() method. The .NET Framework calls IFormattable.ToString() instead of Object.ToString() for every type that implements IFormattable. If you add support for the IFormattable interface and do not support these standard formats, you've broken the automatic string conversions in the .NET Framework.

Overriding Object.ToString() is the simplest way to provide a string representation of your classes. You should provide that every time you create a type. On those rarer occasions when your type is expected to provide more sophisticated output, you should take advantage of implementing the IFormattable interface. It provides the standard way for users of your class to customize the text output for your type. If you leave these out, your users are left with implementing custom formatters (IFormatProvider). Those solutions require more code, and because users are outside of your class, they cannot examine the internal state of the object.

class Item5

{

    public void TestToStringOverride()

    {

        // Create and initialize a Person object

        Person p = new Person();

        p.FirstName = "Yazan";

        p.LastName = "Diranieh";

        p.DOB = new DateTime(2000, 1, 1);

        p.Address = "Street, City, Country";

 

        // Display its string representation

        Trace.WriteLine(p.ToString());          // Output: Diranieh

 

        // Use IFormattable to specify various format string

        // Output: "Yazan    Diranieh    01/01/2000 00:00:00    Street, City, Country    "

        IFormattable fomattable = p as IFormattable;

        if (fomattable != null)

            Trace.WriteLine(fomattable.ToString("FLDA", null)); 

    }

}

 

namespace Item5NS

{

    /// <summary>

    /// The ToString() override uses only the LastName. You address this deficiency by

    /// implementing the IFormattable interface on your type. IFormattable contains an

    /// overloaded ToString() method that lets you specify formatting information for

    /// your type.

    /// </summary>

    internal class Person : IFormattable

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public DateTime DOB { get; set; }

        public string Address { get; set; }

 

        // Override ToString()

        public override string ToString()

        {

            return LastName;

        }

 

        #region IFormattable Members

        /// <summary>

        /// Supported formats are:

        /// F: FirstName

        /// L: LastName

        /// D: DOB

        /// A: Address

        ///

        /// The second parameter to IFormattable.ToString() is an object that implements the

        /// IFormatProvider interface. This object lets clients provide formatting options

        /// that you did not anticipate. No matter how many different formats you support,

        /// your users will one day want some format that you did not anticipate. That's why

        /// the first few lines of the method look for an object that implements IFormatProvider

        /// and delegate the job to its ICustomFormatter. See MSDN for an example of how to

        /// implement IFormatProvider

        /// </summary>

        public string ToString(string format, IFormatProvider formatProvider)

        {

            // See notes above for an explanation of the following block of code

            if (formatProvider != null)

            {

                ICustomFormatter fmt = formatProvider.GetFormat(this.GetType()) as ICustomFormatter;

                if (fmt != null)

                    return fmt.Format(format, this, formatProvider); 

            }

 

            StringBuilder sb = new StringBuilder();

            // Format string form: {index[,alignment][:formatString]}

            foreach (char formatchar in format)

            {

                switch (formatchar)

                {

                    case 'F':

                        sb.AppendFormat("{0}\t", FirstName);

                        break;

                    case 'L':

                        sb.AppendFormat("{0}\t", LastName);

                        break;

                    case 'D':

                        sb.AppendFormat("{0:G}\t", DOB);

                        break;

                    case 'A':

                        sb.AppendFormat("{0}\t", Address);

                        break;

                    case 'G':

                    default:

                        sb.Append(LastName);

                        break;

                }

            } 

            return sb.ToString();

        }

        #endregion

    }

}

6. Distinguish Between Value Types and Reference Types

Value types or reference types? Structs or classes? It's not as simple as preferring one over the other. The right choice depends on how you expect to use the new type. Value types are not polymorphic. They are better suited to storing the data that your application manipulates. Reference types can be polymorphic and should be used to define the behavior of your application. Structs store data. Classes define behavior.

In C#, you declare whether a new type should be a value type or a reference type using the struct or class keywords. Value types should be small, lightweight types. Reference types form your class hierarchy. To start, MyData is used as the return value from a method:

private MyData _myData;
public MyData Foo()
{
    return _myData;
}

// call it:
MyData v = Foo();
TotalSum += v.Value;

If MyData is a value type, the return value gets copied into the storage for v. Furthermore, v is on the stack. However, if MyData is a reference type, you've exported a reference to an internal variable. You've violated the principle of encapsulation (see Item 23 Avoid Returning References to Internal Class Objects)

Or, consider this variant:

private MyData _myData;
public MyData Foo()
{
    return _myData.Clone( ) as MyData;
}

// call it:
MyData v = Foo();
TotalSum += v.Value;

Now, v is a copy of the original _myData. As a reference type, two objects are created on the heap. You don't have the problem of exposing internal data. Instead you've created an extra object on the heap. If v is a local variable, it quickly becomes garbage and Clone() forces you to use runtime type checking. All in all, it's inefficient.

In general, types that are used to export data through public methods and properties should be value types. But that's not to say that every type returned from a public member should be a value type. Now consider this alternative code snippet to the one shown first:

private MyType _myType;
public IMyInterface Foo()
{
    return _myType as IMyInterface;
}

// call it:
IMyInterface iMe = Foo();
iMe.DoWork( );


The _myType variable is still returned from the Foo() method. But this time, instead of accessing the data inside the returned value, the object is accessed to invoke a method through a defined interface. You're accessing the MyType object not for its data contents, but for its behavior. That behavior is expressed through the IMyInterface interface, which can be implemented by multiple different types. For this example, MyType should be a reference type, not a value type. MyType's responsibilities revolve around its behavior, not its data members. That simple code snippet starts to show you the distinction: Value types store values, and reference types define behavior.

Now consider this class:

public class C
{
    private MyType _a = new MyType( );
    private MyType _b = new MyType( );
    ...
}

C var = new C();


How many objects are created? How big are they? It depends. If MyType is a value type, you've made one allocation. The size of that allocation is twice the size of MyType. However, if MyType is a reference type, you've made three allocations: one for the C object, which is 8 bytes (assuming 32-bit pointers), and two more for each of the MyType objects that are contained in a C object. The difference results because value types are stored inline in an object, whereas reference types are not. Each variable of a reference type holds a reference, and the storage requires extra allocation. To drive this point home, consider this allocation:

MyType [] var = new MyType[ 100 ];

If MyType is a value type, one allocation of 100 times the size of a MyType object occurs. However, if MyType is a reference type, one allocation just occurred. Every element of the array is null. When you initialize each element in the array, you will have performed 101 allocations—and 101 allocations take more time than 1 allocation. Allocating a large number of reference types fragments the heap and slows you down. If you are creating types that are meant to store data values, value types are the way to go.

The decision to make a value type or a reference type is an important one. It is a far-reaching change to turn a value type into a class type. Consider this type:

public struct Employee
{
    private string _name;
    private int _ID;
    private decimal _salary;
    ...
    public void Pay( BankAccount b )
    {
        b.Balance += _salary;
    }
}

One day you decide that there are different classes of Employees: Salespeople get commissions, and managers get bonuses. You decide to change the Employee type into a class and make method Pay a virtual method. That breaks much of the existing code that uses your customer struct. Return by value becomes return by reference. Parameters that were passed by value are now passed by reference. The behavior of this little snippet changed drastically:

Employee e1 = Employees.Find( "CEO" );
e1.Salary += Bonus;                     // Add one time bonus.
e1.Pay( CEOBankAccount );

What was a one-time bump in pay to add a bonus just became a permanent raise as now e1 is a reference and changes to e1 will remain persistent.

The documentation for .NET recommends that you consider the size of a type as a determining factor between value types and reference types. In reality, a much better factor is the use of the type. Types that are simple structures or data carriers are excellent candidates for value types. Value types are more efficient in terms of memory management: There is less heap fragmentation, less garbage, and less indirection. More important, value types are copied when they are returned from methods or properties. There is no danger of exposing references to internal structures. But you pay in terms of features. Value types have very limited support for common object-oriented techniques. You cannot create object hierarchies of value types. You should consider all value types as though they were sealed. You can create value types that implement interfaces, but that requires boxing, which Item 17 (Minimize Boxing and Unboxing) shows causes performance degradation. Think of value types as storage containers, not objects in the OO sense.

You'll create more reference types than value types. If you answer yes to all these questions, you should create a value type. Compare these to the previous Employee example:

  1. Is this type's principal responsibility data storage?

  2. Is its public interface defined entirely by properties that access or modify its data members?

  3. Am I confident that this type will never have subclasses?

  4. Am I confident that this type will never be treated polymorphically?

Build low-level data storage types as value types. Build the behavior of your application using reference types. You get the safety of copying data that gets exported from your class objects. You get the memory usage benefits that come with stack-based and inline value storage, and you can utilize standard object-oriented techniques to create the logic of your application. When in doubt about the expected use, use a reference type.

7. Prefer Immutable Atomic Value Types

Immutable types are simple: After they are created, they are constant. You cannot change the object's internal state to make it invalid. Immutable types are inherently thread safe: Multiple readers can access the same contents. There is no chance for different threads to see inconsistent views of the data.

Atomic types are types that describe a single entity. An address is a single thing, composed of multiple related fields. A change in one field likely means changes to other fields. A customer type is not an atomic type. A customer type will likely contain many pieces of information: an address, a name, and one or more phone numbers. Any of these independent pieces of information might change. class Address_Mutable and method TestMutableTypes() illustrate the issues with mutable types, while class Address_Immutable and method TestImmutableType() show how to fix these problems.

In general, to create an immutable type, you need to ensure that there are no holes that would allow clients to change your internal state. Value types do not support derived types, so you do not need to defend against derived types modifying fields. But you do need to watch for any fields in an immutable type that are mutable reference types. When you implement your constructors for these types, you need to make a defensive copy of that mutable type. See classes BookList_Mutable and BookList_Immutable and method TestImmutableTypesWithMutableFields().

The complexity of a type dictates which of three strategies you will use to initialize your immutable type. Defining the reasonable set of constructors is often the simplest approach. You can also create factory methods to initialize the structure. Factories make it easier to create common values. The .NET Framework Color type follows this strategy to initialize system colors. The static methods Color.FromKnownColor() and Color.FromName() return a copy of a Color value that represents the current value for a given system Color. Third, you can create a mutable companion class for those instances in which multistep operations are necessary to fully construct an immutable type. The .NET string class follows this strategy with the System.Text.StringBuilder class. You use the StringBuilder class to create a string using multiple operations. After performing all the operations necessary to build the string, you retrieve the immutable string from the StringBuilder.

Immutable types are simpler to code and easier to maintain. Do not blindly create get and set accessors for every property in your type. Your first choice for types that store data should be immutable, atomic value types. You easily can build more complicated structures from these entities.

class Item7

{

    /// <summary>

    /// This method illustrate some of the issues around using mutable types. After an

    /// instance is created and initialized, we modify the address. This code looks harmless

    /// enough, but suppose that this fragment is part of a multithreaded program. Any

    /// context switch after the city changes and before the country changes would leave

    /// the potential for another thread to see an inconsistent view of the data.

    ///

    /// You can still get in trouble even if not writing multithreaded programs. Imagine that

    /// the country was invalid and the set threw an exception. You've made only some of the

    /// changes you intended, and you've left the system in an invalid state. To fully implement

    /// exception safety, you would need to create defensive copies around any code block in

    /// which you change more than one field. Thread safety would require adding significant

    /// thread-synchronization checks on each property accessor, both sets and gets. Instead,

    /// make the Address structure an immutable type. See method TestImmutableType()

    /// </summary>

    public void TestMutableTypes()

    {

        // Create and initialize and instance of Address_Mutable

        Address_Mutable a1 = new Address_Mutable();

        a1.Line1 = "1 Les Champes Elysee";

        a1.City = "Paris";

        a1.Country = "France";

 

        // Change address

        a1.Line1 = "1 Bond Street";

        a1.City = "London";         // Country is now invalid

        a1.Country = "England";     // Country is now valid

    }

 

    /// <summary>

    /// Shows the use of immutable types. Note that as soon as a new Address_Immutable

    /// object is constructed, its value is fixed for all time. It's exception safe:

    /// object 'address' has either its original value or its new value. If an exception

    /// is thrown during the construction of the new 'address' object, the original value

    /// of 'address' object is unchanged.

    /// </summary>

    public void TestImmutableType()

    {

        // Create an immutable object

        Address_Immutable address = new Address_Immutable("1 Les Champes Elysee", "Paris", "France");

 

        // Cannot moditfy the state of the 'address' object. Attempting to set any

        // field on object 'address' gives error CS0272: "The property or indexer

        // 'Address_Immutable.City' cannot be used in this context because the set

        // accessor is inaccessible"

 

        // To change 'address', you must re-initialize

        address = new Address_Immutable("1 Bond Street", "London", "England");           

    }

 

    public void TestImmutableTypesWithMutableFields()

    {

        // Create a list of books

        List<Book> lstBooks = new List<Book> { new Book("Book1", "Author1"), new Book("Book2", "Author2") };

 

        // Create a mutable type that returns a mutable reference type

        BookList_Mutable bm = new BookList_Mutable(lstBooks);

 

        // Create an immutable type that returns a mutable reference type

        BookList_Immutable bim = new BookList_Immutable(lstBooks);

 

        // Modify the book list. Also modified the internals of the supposedly immutable

        // 'bm' objects. But does not modify the internal of the immutable 'bim' object

        lstBooks[0] = new Book("Book3", "Author3");

 

        // Examine contents of 'bm' and 'bim' and note that the internals of 'bm'

        // have changed, while the internals of 'bim' have not changed!

    }

}

 

namespace Item7NS

{

    /// <summary>

    /// Address_Mutable is a mutable type; its data can be changed after an instance

    /// is created. Note the use of 'struct' rather than a 'class' as Address_Mutable

    /// is a data storage with no behavior

    ///

    /// Note the call to the default constructor in the constructor initializer.

    /// This is required in order to assign a value to an automatically-implemented property

    /// from a constructor. Otherwise, you get error CS0188: "The 'this' object cannot be used

    /// before all of its fields are assigned to."

    /// </summary>

    struct Address_Mutable

    {

        public string Line1 { get; set; }

        public string City { get; set; }

        public string Country { get; set; }

    }

 

    struct Address_Immutable

    {

        public Address_Immutable(string line, string city, string country)  : this()

        {

            Line1 = line;

            City = city;

            Country = country;

 

        }

        public string Line1 { get; private set; }

        public string City { get; private set; }

        public string Country { get; private set; }

    }

  

    struct BookList_Mutable

    {

        private readonly List<Book> _lstBooks;

        public BookList_Mutable(List<Book> books)

        {

            // Mutable! _lstBook modified if books is modified

            _lstBooks = new List<Book>();

            _lstBooks = books;     

        }

 

        public List<Book> Books { get { return _lstBooks; } }

    }

 

    struct BookList_Immutable

    {

        private readonly List<Book> _lstBooks;

        public BookList_Immutable(List<Book> books)

        {

            // Immutable! Copies values because Book is a struct (value type)

            // Therefore, _lstBook is not modified if books is modified

            _lstBooks = new List<Book>();

            _lstBooks.AddRange(books);

        }

 

        public List<Book> Books { get { return _lstBooks; } }

    }

 

    struct Book

    {

        public string Name { get; private set; }

        public string Author { get; private set; }

 

        public Book(string name, string auth) : this()

        {

            Name = name;

            Author = auth;

        }

    }

}

8. Ensure That 0 Is a Valid State for Value Types

The default .NET system initialization sets all objects to all 0s. There is no way for you to prevent other programmers from creating an instance of a value type that is initialized to all 0s. Make 0 the default value for your type.

One special case is enums. Never create an enum that does not include 0 as a valid choice. All enums are derived from System.ValueType. The values for the enumeration start at 0, but you can modify that behavior. See TestEnums() method below.

class Item8

{

    private enum Planet_Bad_NoDefault

    {

        // Explicitly assign values. Default starts at 0 otherwise.

        Mercury = 1,

        Venus = 2,

        Earth = 3,

        Mars = 4,

        Jupiter = 5,

        Saturn = 6,

        Neptune = 7,

        Uranus = 8,

        Pluto = 9

    }

 

    private enum Planet_Good_HasDefault

    {

        None = 0,

        Mercury = 1,

        Venus = 2,

        Earth = 3,

        Mars = 4,

        Jupiter = 5,

        Saturn = 6,

        Neptune = 7,

        Uranus = 8,

        Pluto = 9

    }

  

    /// <summary>

    /// In this function, sphere object is 0, which is not a valid value as per the definition

    /// of enum Plant. Any code that relies on the (normal) fact that enums are restricted to

    /// the defined set of enumerated values won't work. When you create your own values for an

    /// enum, make sure that 0 is one of them. If you use bit patterns in your enum, define 0

    /// to be the absence of all the other properties.

    /// </summary>

    public void TestEnums()

    {

        // bad is zero, which is not valid!

        Planet_Bad_NoDefault bad = new Planet_Bad_NoDefault();

        string name1 = Enum.GetName(typeof(Planet_Bad_NoDefault), bad);     // name is null

 

        // good is 1, which is valid

        Planet_Good_HasDefault good = new Planet_Good_HasDefault();

        string name2 = Enum.GetName(typeof(Planet_Good_HasDefault), bad);   // name = "None"

    }

}

9. Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator==

C# provides many different functions that determine whether two different objects are "equal":

public static  bool  ReferenceEquals ( object left, object right );
public static  bool  Equals( object left, object right );
public virtual bool  Equals( object right);
public static  bool  operator==( MyClass left, MyClass right );


The general rules regrinding these functions are: You should never redefine the first two static functions. You'll often create your own instance Equals() method to define the semantics of your type, and you'll occasionally override operator==(), but only for performance reasons in value types. Furthermore, there are relationships among these four functions, so when you change one, you can affect the behavior of the others.

Recall that two variables of a reference type are equal if they refer to the same object, referred to as object identity. Two variables of a value type are equal if they are the same type and they contain the same contents. See methods TestReferenceEquality() and TestObjectEquals() below.

Before discussing the methods you will override, you need to make sure that your definition and implementation of 'equality' are consistent with other programmers' expectations. This means that you need to keep in mind the mathematical properties of equality: Equality is reflexive, symmetric, and transitive. The reflexive property means that any object is equal to itself. No matter what type is involved, a == a is always true. The symmetric property means that order does not matter: If a == b is true, b == a is also true. If a == b is false, b == a is also false. The last property is that if a == b and b == c are both true, then a == c must also be true. That's the transitive property. See notes for TestEquals() method below.

Anytime you create a value type, redefine operator==(). The reason is exactly the same as with the instance Equals() function. The default version uses reflection to compare the contents of two value types. That's far less efficient than any implementation that you would write, so write your own.

class Item9

{

    /// <summary>

    /// This is a function that should never be redefined. It does the right thing.

    /// Object.ReferenceEquals() returns true if two variables refer to the same object—that is,

    /// the two variables have the same object identity. Whether the types being compared are

    /// reference types or value types, this method always tests object identity, not object

    /// contents. Yes, that means that ReferenceEquals() always returns false when you use it

    /// to test equality for value types. Even when you compare a value type to itself,

    /// ReferenceEquals() returns false. This is due to boxing as ReferenceEquals operates

    /// only on reference types, hence when value types are used with ReferenceEquals, boxing

    /// creates new reference obejcts

    /// </summary>

    public void TestReferenceEquality()

    {

        // Reference types

        Order o1 = new Order(1);

        Order o2 = new Order(2);

        bool b1 = object.ReferenceEquals(o1, o1);   // TRUE

        bool b2 = object.ReferenceEquals(o1, o2);   // FALSE

 

        // Reference types. String is a special case. Two strings having the same string

        // always refer to the same object

        string s1 = "Test1";

        string s2 = "Test1";

        b1 = object.ReferenceEquals(s1, s1);        // TRUE

        b2 = object.ReferenceEquals(s1, s2);        // TRUE

 

        // Value types

        int n1 = 10;

        int n2 = 10;

        b1 = object.ReferenceEquals(n1, n1);        // FALSE

        b2 = object.ReferenceEquals(n1, n2);        // FALSE

    }

 

    /// <summary>

    /// This is the second function that you should never redefine. This method tests

    /// whether two variables are equal when you don't know the runtime type of the two

    /// arguments.

    ///

    /// Recall that value types and reference types are instances of System.Object. So how

    /// does this method test the equality of two variables, without knowing their type,

    /// when equality changes its meaning depending on the type? The answer is simple:

    /// This method delegates that responsibility to one of the types in question. The

    /// static Object.Equals() method. For now, understand that static Equals() uses the

    /// instance Equals() method of the left argument to determine whether two objects are

    /// equal.

    ///

    /// </summary>

    public void  TestObjectEquals()

    {    }

 

    /// <summary>

    ///  The default Object.Equals() function behaves exactly the same as Object.ReferenceEquals().

    ///  However, System.ValueType does override Object.Equals().  Two variables of a value type

    ///  are equal if they are the same type and they have the same contents. ValueType.Equals()

    ///  implements that behavior. Unfortunately, ValueType.Equals() does not have an efficient

    ///  implementation. as it must compare all the member variables in any derived type, without

    ///  knowing the runtime type of the object. In C#, that means using reflection. As you'll see

    ///  in Item 44, there are many disadvantages to reflection, especially when performance is

    ///  a goal. Equality is one of those fundamental constructs that gets called frequently in

    ///  programs, so performance is a worthy goal. Under almost all circumstances, you can write

    ///  a much faster override of Equals() for any value type. The recommendation is simple:

    /// 

    ///  For Value Types:

    ///  ALWAYS CREATE AN OVERRIDE OF ValueType.Equals() WHENEVER YOU CREATE A VALUE TYPE.

    /// 

    /// For Reference Types:

    /// OVERRIDE THE INSTANCE Equals() FUNCTION ONLY WHEN YOU WANT TO CHANGE THE DEFINED SEMANTICS

    /// FOR A REFERENCE TYPE.  

    ///

    /// For reference types, your instance method needs to follow a certain pattern to avoid

    /// strange surprises for users of your class. See Equals() override in class Order below

    ///

    ///  Note that overriding Equals() means that you should write an override for GetHashCode().

    ///  See Item 10 for details.

    /// 

    /// </summary>

    public void TestEquals()

    {     }

}

 

namespace Item9NS

{

    public class Order

    {

        public int OrderID { get; set; }

        public Order(int id) { OrderID = id; }

        public void ProcessOrder() { }

 

        /// <summary>

        /// Equals() should never throw exceptions—it doesn't make much sense. Two variables

        /// are or are not equal; there's not much room for other failures. Just return false

        /// for all failure conditions, such as null references or the wrong argument types.

        ///

        /// The check using GetType() determines whether the two objects being compared are the

        /// same type. The exact form is important. First, notice that it does not assume that

        /// 'this' is of type Order; it calls this.GetType(). The actual type might be a class

        /// derived from Order. Second, the code checks the exact type of objects being compared.

        /// It is not enough to ensure that you can convert the right-side parameter to the current

        /// type.

        /// </summary>

        public override bool Equals(object rhs)

        {

            // check that right-hand side of the equality is not null. For the the 'this' pointer

            // it is never null in C# methods as the CLR throws an exception before calling any

            // method on a null reference

            if (rhs == null)

                return false;

 

            // Check we are not comparing the same object references

            if (object.ReferenceEquals(this, rhs))

                return true;

 

            // Checks that the compared objects are of the same type.

            if (this.GetType() != rhs.GetType())

                return false;

 

            // Compare this type's contents here:

            return (this.OrderID == (rhs as Order).OrderID);

        }

    }

}

10. Understand the Pitfalls of GetHashCode()

GetHashCode() is one function that you should avoid writing. GetHashCode() is used in one place only: to define the hash value for keys in a hash-based collection, typically the Hashtable or Dictionary containers. If you are defining a type that will never be used as the key in a container, then there is no need to write GetHashCode().

The following discussion applies only if you ever need to create a type that is meant to be used as a hash table key. Hash-based containers use hash codes to optimize searches. Every object generates an integer value called a hash code. Objects are stored in buckets based on the value of that hash code. To search for an object, you request its key and search just that one bucket. In .NET, every  object has a hash code, determined by System.Object.GetHashCode(). Any overload of GetHashCode() must follow these three rules:

  1. If two objects are equal (as defined by operator==), they must generate the same hash value. Otherwise, hash codes can't be used to find objects in containers.

  2. For any object A, A.GetHashCode() must be an instance invariant. No matter what methods are called on A, A.GetHashCode() must always return the same value. That ensures that an object placed in a bucket is always in the right bucket.

  3. The hash function should generate a random distribution among all integers for all inputs. That's how you get efficiency from a hash-based container.

Writing a correct and efficient hash function requires extensive knowledge of the type to ensure that rule 3 is followed. The versions defined in System.Object and System.ValueType do not have that advantage. These versions must provide the best default behavior with almost no knowledge of your particular type. Object.GetHashCode() uses an internal field in the System.Object class to generate the hash value. Each object created is assigned a unique object key, stored as an integer, when it is created. These keys start at 1 and increment every time a new object of any type gets created. The object identity field is set in the System.Object constructor and cannot be modified later. Object.GetHashCode() returns this value as the hash code for a given object.

GetHashCode() for reference types: If two objects are equal, Object.GetHashCode() returns the same hash value, unless you've overridden operator==. System.Object's version of operator== tests object identity. GetHashCode() returns the internal object identity field. It works. However, if you've supplied your own version of operator==, you must also supply your own version of GetHashCode() to ensure that the first rule is followed. See Item 9 (Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator==) for details on equality.

The second rule is followed: After an object is created, its hash code never changes.

The third rule, a random distribution among all integers for all inputs, does not hold. A numeric sequence is not a random distribution among all integers unless you create an enormous number of objects. The hash codes generated by Object.GetHashCode() are concentrated at the low end of the range of integers.

This means that Object.GetHashCode() is correct but not efficient. If you create a hashtable based on a reference type that you define, the default behavior from System.Object is a working, but slow, hashtable. When you create reference types that are meant to be hash keys, you should override GetHashCode() to get a better distribution of the hash values across all integers for your specific type.

If you're going to build a better hash code, you need to place some constraints on your type. Examine the three rules again, this time in the context of building a working implementation of GetHashCode().

First, if two objects are equal, as defined by operator==, they must return the same hash value. Any property or data value used to generate the hash code must also participate in the equality test for the type. Obviously, this means that the same properties used for equality are used for hash code generation. It's possible to have properties participate in equality that are not used in the hash code computation.

The second rule is that the return value of GetHashCode() must be an instance invariant. See TestHashCode1() which creates objects whose hash code is not instance invariant.

The third rule says that GetHashCode() should generate a random distribution among all integers for all inputs. A common and successful algorithm is to XOR all the return values from GetHashCode() on all fields in a type. If your type contains some mutable fields, exclude those fields from the calculations.

To summarize, GetHashCode() has very specific requirements: Equal objects must produce equal hash codes, and hash codes must be object invariants and must produce an even distribution to be efficient. All three can be satisfied only for immutable types. For other types, rely on the default behavior, but understand the pitfalls.

class Item10

{

    public void TestHashCode1()

    {

        // Create an object and show its hash code

        Customer c1 = new Customer("Test");

        Trace.WriteLine("c1 hash code:" + c1.GetHashCode());        // c1 hash code:-354185577

 

        // Changing the state of c1 changes its hash code! This violates the second

        // rule which states that GetHashCode() must be instance invariant

        c1.Name = "Test2";

        Trace.WriteLine("c1 hash code:" + c1.GetHashCode());        // c1 hash code:-1556460257

    }

}

 

namespace Item10NS

{

    public class Customer

    {

        public string Name { get; set; }

        public Customer(string name)

        {

            Name = name;

        }

 

        public override int GetHashCode()

        {

            return Name.GetHashCode();

        }

    }

}

11. Prefer foreach Loops

The C# foreach statement generates the best iteration code for any collection you have. C# code runs in a safe, managed environment. Every memory location is checked, including array indexes. Therefore, when you iterate collections, use foreach instead of other looping constructs. Consider the following C++ style loop:

int len = foo.Length;
for (int index = 0; index < len; index++ )
    Trace.WriteLine( index );


By hosting the Length variable out of the loop, you make a change that hinders the JIT compiler's chance to remove range checking inside the loop. One of the CLR guarantees is that you cannot write code that overruns the memory that your variables own. The runtime generates a test of the actual array bounds (not your len variable) before accessing each particular array element. You get one bounds check for the price of two.

foreach adds other language benefits for you. The loop variable is read-only: You can't replace the objects in a collection using foreach. Also, there is explicit casting to the correct type. If the collection contains the wrong type of objects, the iteration throws an exception. foreach gives you similar benefits for multidimensional arrays. Suppose that you are creating a chess board. You would write these two fragments:

Square[,] _theBoard = new Square[ 8, 8 ];
for (int i = 0; i < _theBoard.GetLength( 0 ); i++ )
    for(int j = 0; j < _theBoard.GetLength( 1 ); j++ )
        _theBoard[ i, j ].PaintSquare( );


Instead, you can simplify painting the board this way:

foreach(Square sq in _theBoard )
    sq.PaintSquare( );

To summarize, foreach is a very versatile statement. It generates the right code for upper and lower bounds in arrays, iterates multidimensional arrays, coerces the operands into the proper type (using the most efficient construct), and, on top of that, generates the most efficient looping constructs. It's the best way to iterate collections. With it, you'll create code that is more likely to last, and it's simpler for you to write in the first place. It's a small productivity improvement, but it adds up over time.

Resource Management

12. Prefer Variable Initiailzers to Assignment Statements

Classes often have more than one constructor. Over time, it's easy for the member variables and the constructors to get out of synch. The best way to make sure this doesn't happen is to initialize variables where you declare them instead of in the body of every constructor. You should utilize the initializer syntax for both static and instance variables. Initializers execute before the base class constructor for your type executes, and they are executed in the order the variables are declared in your class.

Regardless of the number of constructors you eventually add to your types, member variables initialized via variables initializers will be initialized properly. The compiler generates code at the beginning of each constructor to execute all the initializers you have defined for your instance member variables. When you add a new constructor, all member variables get initialized. Similarly, if you add a new member variable, you do not need to add initialization code to every constructor; initializing the variable where you define it is sufficient. Equally important, the initializers are added to the compiler-generated default constructor. The C# compiler creates a default constructor for your types whenever you don't explicitly define any constructors.

Using initializers is the simplest way to avoid uninitialized variables in your types, but it's not perfect. In three cases, you should not use the initializer syntax:

  1. Do not use variable initializers when you are initializing the object to 0, or null. The default system initialization sets everything to 0 for you before any of your code executes. The system-generated 0 initialization is done at a very low level using the CPU instructions to set the entire block of memory to 0. Any extra 0 initialization on your part is superfluous.

  2. Do not use variable initializers when you create multiple initializations for the same object. You should use the initializer syntax only for variables that receive the same initialization in all constructors. See class MyClass2. By the way, the proper way to fix MyClass2 is to use constructor chaining (14. Use Constructor Chaining). See MyClass3.

  3. Do not use variable initializers when you want to facilitate exception handling. You cannot wrap the initializers in a try block. Any exceptions that might be generated during the construction of your member variables get propagated outside of your object.

To summarize, variable initializers are the simplest way to ensure that the member variables in your type are initialized regardless of which constructor is called. The initializers are executed before each constructor you make for your type. Using this syntax means that you cannot forget to add the proper initialization when you add new constructors for a future release.

namespace Item12NS

{

    /// <summary>

    /// Illustrates use of variables initializers

    /// </summary>

    internal class MyClass

    {

        // Declare a collection and initialize it

        List<int> _lstNumbers = new List<int>();

    }

 

    /// <summary>

    /// When you create a new MyClass2, specifying the size of the collection, you create

    /// two lists: The list initialized via variable initializer is immediately garbage as

    /// the variable initializer executes before every constructor. The constructor body

    /// creates the second list. See MyClass3 which utilizes constructor chaining to fix

    /// MyClass2

    /// </summary>

    internal class MyClass2

    {

        List<int> _lst = new List<int>();

 

        public MyClass2()

        { /* Empty Implementation */ }

 

        public MyClass2(int nCapacity)

        {

            _lst = new List<int>(nCapacity);

        }

    }

 

    /// <summary>

    /// Fixes MyClass2 by utilizing constructor chaining

    /// </summary>

    internal class MyClass3

    {

        List<int> _lst = null;

 

        public MyClass3() : this(0)

        { /* Empty Implementation */ }

 

        public MyClass3(int nCapacity)

        {

            _lst = (nCapacity == 0)? new List<int>() : new List<int>(nCapacity);

        }

    } 

}

13. Initialize Static Class Members with Static Constructors

C# lets you use static initializers and a static constructor to initialize static member variables. A static constructor is a special function that executes before any other methods, variables, or properties defined in that class are accessed. You use this function to initialize static variables, enforce the singleton pattern, or perform any other necessary work before a class is usable. You should not use your instance constructors, some special private function, or any other idiom to initialize static variables.

As with instance initialization, you can use the initializer syntax as an alternative to the static constructor. If you simply need to allocate a static member, use the initializer syntax. When you have more complicated logic to initialize static member variables, create a static constructor.

As with instance initializers, the static initializers are called before any static constructors are called. And, yes, your static initializers execute before the base class's static constructor. Implementing the singleton pattern in C# is the most frequent use of a static constructor. Make your instance constructor private, and add an initializer. See class MySingleton below.

The CLR calls your static constructor automatically when your type is first loaded into an application space. You can define only one static constructor, and it must not take any arguments. Because static constructors are called by the CLR, you must be careful about exceptions generated in them. If you let an exception escape a static constructor, the CLR will terminate your program. Exceptions are the most common reason to use the static constructor instead of static initializers.

namespace Item13NS

{

    /// <summary>

    /// Illustrates the use of a static initializer to implement a singleton.

    /// An alternative implementation is to move the static initializer to a static

    /// constructor, in case you have more complex logic to initialize the singleton,

    /// or where you may need to catch any generated exceptions.

    /// </summary>

    internal class MySingleton

    {

        // Static initializer.

        private static readonly MySingleton _instance = new MySingleton();

 

        private MySingleton()

        { /* Empty Implementation */ }

 

        public static MySingleton SingleInstance { get { return _instance; } }

    }

}

14. Use Constructor Chaining

When you find that multiple constructors contain the same logic, factor that logic into a common constructor (and not a private method) using constructor initializers. Constructor initializers allow one constructor to call another constructor. The C# compiler recognizes the constructor initializer as special syntax and removes the duplicated variable initializers and the duplicated base class constructor calls. The result is that your final object executes the minimum amount of code to properly initialize the object. You also write the least code by delegating responsibilities to a common constructor. See MyClass class below.

C# does not support default parameters, which would be the preferred C++ solution to this problem. You must write each constructor that you support as a separate function. With constructors, that can mean a lot of duplicated code. Use constructor chaining instead of creating a common utility routine.

Now look at MyClass_Bad class below. That version looks the same as MyClass class, but it generates far less efficient object code. The compiler adds code to perform several functions on your behalf in constructors. It adds statements for all variable initializers (see Item 12 Prefer Variable Initiailzers to Assignment Statements). It calls the base class constructor. When you write your own common utility function, the compiler cannot factor out this duplicated code.

Another important factor to favour the use of constructor initializers is read-only. The use of a common private function that factors out common constructor logic to initialize readonly constants generates compiler error (see MyClass_Bad). Again, C# constructor initializers provide a better alternative.

As a reminder, here is the order of operations for constructing the first instance of a type:

  1. Static variable storage is set to 0.

  2. Static variable initializers execute.

  3. Static constructors for the base class execute.

  4. The static constructor executes.

  5. Instance variable storage is set to 0.

  6. Instance variable initializers execute.

  7. The appropriate base class instance constructor executes.

  8. The instance constructor executes.

Subsequent instances of the same type start at step 5 because the class initializers execute only once. Also, steps 6 and 7 are optimized so that constructor initializers cause the compiler to remove duplicate instructions.

namespace Item14NS

{

    /// <summary>

    /// Illustrates usage of constructor initializers

    /// </summary>

    class MyClass

    {

        private List<int> _lstNumbers = null;

        private string _strName = null;

 

        // Constructors

        public MyClass(string name, int capacity)

        {

            _lstNumbers = (capacity > 0) ? new List<int>(capacity) : new List<int>();

            _strName = name;

        }

 

        public MyClass() : this( string.Empty, 0)

        { /* Empty Implementation */ }

 

        public MyClass(string name) : this( name, 0 )

        { /* Empty Implementation */ }

 

        public MyClass( int capacity): this( string.Empty, capacity)

        { /* Empty Implementation */ }

    }

 

    class MyClass_Bad

    {

        private List<int> _lstNumbers = null;

        private string _strName = null;

        private readonly int _nValue;

 

        // Constructors

        public MyClass_Bad(string name, int capacity)

        {

            ConstructorCommon(name, capacity);

        }

 

        public MyClass_Bad()

        {

            ConstructorCommon( string.Empty, 0);

        }

 

        public MyClass_Bad(string name)

        {

            ConstructorCommon( name, 0 );

        }

 

        public MyClass_Bad(int capacity)

        {

            ConstructorCommon(string.Empty, capacity);

        }

 

        private void ConstructorCommon(string name, int capacity)

        {

            _lstNumbers = (capacity > 0) ? new List<int>(capacity) : new List<int>();

            _strName = name;

 

            // error CS0191: A readonly field cannot be assigned to (except in a constructor

            // or a variable initializer)

            // _nValue = -1;

        }

    }

}

15. Utilize using and try / finally for Resource Cleanup

Types that use unmanaged system resources should be explicitly released using the Dispose() method of the IDisposable interface. Therefore, anytime you use types that have a Dispose() method, it's your responsibility to release those resources by calling Dispose(). The best way to ensure that Dispose() always gets called is to utilized the using statement or a try/finally block.

All types that own unmanaged resources implement the IDisposable interface. In addition, they defensively create a finalizer for those times when you forget to dispose properly. See TestResourceCleanup1(), TestResourceCleanup2() and TestResourceCleanup3() methods below.

Whenever you use one disposable object in a function, the using clause is the simplest method to use to ensure that objects get disposed of properly. The using statement generates a try / finally block around the object being allocated.

Note that the using statement works only if the compile-time type supports the IDisposable interface. You cannot use it with arbitrary objects. A quick defensive as clause is all you need to safely dispose of objects that might or might not implement IDisposable:

//Object may or may not support IDisposable.
object obj = Factory.CreateResource( );
using ( obj as IDisposable )
{
    Console.WriteLine( obj.ToString( ));
}


If obj implements IDisposable, the using statement generates the cleanup code. If not, the using statement degenerates to using(null), which is safe but doesn't do anything.

Every using statement creates a new nested try / finally block. I find that an ugly construct, so when I allocate multiple objects that implement IDisposable, I prefer to write my own try / finally blocks. See TestResourceCleanup4() below method.

class Item15

{

    /// <summary>

    /// Illustrates that two disposable objects (reader and writer) are not properly

    /// cleaned up. Both of these objects remain in memory until their finalizers are

    /// called. See TestResourceCleanup2() which adds clean up code

    /// </summary>

    public void TestResourceCleanup1()

    {       

        // Create two disposable streams

        StreamReader reader = new StreamReader(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12.cs");

        StreamWriter writer = new StreamWriter(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12_Copy.cs");

 

        // Use the stream.

        string strContents = reader.ReadToEnd();

        writer.Write(strContents);

 

        // No clean up code!

    }

 

    /// <summary>

    /// Improves TestResourceCleanup1 by cleaning up. However, if exceptions are thrown

    /// while creating or using the streams, calls to Dispose will never be happen.

    /// See TestResourceCleanup3() which fixes this problem through the 'using' statement

    /// </summary>

    public void TestResourceCleanup2()

    {

        // Create two disposable streams

        StreamReader reader = new StreamReader(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12.cs");

        StreamWriter writer = new StreamWriter(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12_Copy.cs");

 

        // Use the stream.

        string strContents = reader.ReadToEnd();

        writer.Write(strContents);

 

        // Clean up code

        reader.Dispose();

        writer.Dispose();

    }

 

    /// <summary>

    /// Improves TestResourceCleanup2 through the use of 'using' statements. You allocate

    /// an object inside a using statement, and the C# compiler generates a try/finally

    /// block around each object

    /// </summary>

    public void TestResourceCleanup3()

    {

        // Create two disposable streams

        using (StreamReader reader = new StreamReader(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12.cs"))

        {

            using (StreamWriter writer = new StreamWriter(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12_Copy.cs"))

            {

 

                // Use the stream.

                string strContents = reader.ReadToEnd();

                writer.Write(strContents);

            }   // calls writer.Dispose()

 

        }   // calls reader.Dispose()

    }

 

    /// <summary>

    /// More readable alternative to TestResourceCleanup() that uses try/finally blocks

    /// </summary>

    public void TestResourceCleanup4()

    {

        StreamReader reader = null;

        StreamWriter writer = null;

        try

        {

            // Create two disposable streams

            reader = new StreamReader(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12.cs");

            writer = new StreamWriter(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12_Copy.cs");

 

            // Use the stream.

            string strContents = reader.ReadToEnd();

            writer.Write(strContents);

        }

        finally

        {

            // Clean up code

            if (reader != null) reader.Dispose();

            if (writer != null) writer.Dispose();

        }

    }

 

    /// <summary>

    /// Improper use of 'using'. The StreamReader object never gets disposed if the

    /// StreamWriter() constructor throws an exception. You must make sure that any

    /// objects that implement IDisposable are allocated inside the scope of a using

    /// block or a try block. Otherwise, resource leaks can occur.

    /// </summary>

    public void TestResourceCleanup5()

    {

        // Create two disposable streams

        StreamReader reader = new StreamReader(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12.cs");

        StreamWriter writer = new StreamWriter(@"D:\Projects\EffectiveCS\EffecitveCS\ResourceManagement\Item12_Copy.cs");

 

        using (reader as IDisposable)

        using (writer as IDisposable)

        {

            // Use the stream.

            string strContents = reader.ReadToEnd();

            writer.Write(strContents);

        }

    }

}

16. Minimize Garbage

You can introduce serious performance drains on your program by creating an excessive number of reference objects that are local to your methods. So do not overwork the garbage collector. You can follow some simple techniques to minimize the amount of work that the Garbage Collector needs to do on your program's behalf. All reference types, even local variables, are allocated on the heap. Every local variable of a reference type becomes garbage as soon as that function exits. One very common bad practice is to allocate GDI objects in a Windows paint handler:

protected override void OnPaint( PaintEventArgs e )
{
    // Bad. Created the same font every paint event.
    using ( Font MyFont = new Font( "Arial", 10.0f ))
    {
        e.Graphics.DrawString( DateTime.Now.ToString(),
        MyFont, Brushes.Black, new PointF( 0,0 ));
    }
    base.OnPaint( e );
}

OnPaint() gets called frequently. Every time it gets called, you create another Font object that contains the exact same settings. The Garbage Collector needs to clean those up for you every time. That's incredibly inefficient. Instead, promote the Font object from a local variable to a member variable. Reuse the same font each time you paint the window:

private readonly Font _myFont = new Font( "Arial", 10.0f );
protected override void OnPaint( PaintEventArgs e )
{
    e.Graphics.DrawString( DateTime.Now.ToString( ),
    _myFont, Brushes.Black, new PointF( 0,0 ));
    base.OnPaint( e );
}

Approach one: promote local variables to member variables when they are reference types (value types don't matter) and they will be used in routines that are called very frequently. The font in the paint routine makes an excellent example. Only local variables in routines that are frequently accessed are good candidates. Infrequently called routines are not. You're trying to avoid creating the same objects repeatedly, not turn every local variable into a member variable

The static property Brushes.Black, used earlier illustrates another technique that you should use to avoid repeatedly allocating similar objects.  Approach two: create static member variables for commonly used instances of the reference types you need.

The first approach of creating a black brush as a member of each of your types helps, but it doesn't go far enough. Programs might create dozens of windows and controls, and would create dozens of black brushes. The .NET Framework designers anticipated this and created a single black brush for you to reuse whenever you need it. The Brushes class contains a number of static Brush objects, each with a different common color. Internally, the Brushes class uses a lazy evaluation algorithm to create only those brushes you request. A simplified implementation looks like this:

private static Brush _blackBrush;
public static Brush Black
{
    get
    {
        // Create a brush once and only when needed
        if ( _blackBrush == null )
            _blackBrush = new SolidBrush( Color.Black );
        return _blackBrush;
    }
}


The last technique involves building the final value for immutable types. The System.String class is immutable: After you construct a string, the contents of that string cannot be modified. Whenever you write code that appears to modify the contents of a string, you are actually creating a new string object and leaving the old string object as garbage. This seemingly innocent practice:

// The += method on the string class creates a new string object and returns that string
string msg = "Hello, ";
msg += thisUser.Name;                     // Creates a new string
msg += ". Today is ";                     // Creates a new string
msg += System.DateTime.Now.ToString();    // Creates a new string


is just as inefficient as if you had written this:

string msg = "Hello, ";
// Not legal, for illustration only:
string tmp1 = new String( msg + thisUser.Name );
string msg = tmp1;                     // "Hello " is garbage.
string tmp2 = new String( msg + ". Today is " );
msg = tmp2;                            // "Hello <user>" is garbage.
string tmp3 = new String( msg + DateTime.Now.ToString( ) );
msg = tmp3;                           // "Hello <user>. Today is " is garbage

For simple constructs such as the previous one you can use the string.Format() method, or for more complicated string operations, you can use StringBuilder().

Therefore, the three techniques that you can use to avoid creating excessive objects are:

  1. Consider promoting local variables to member variables.

  2. Create static objects of the most common instances of your types

  3. Consider creating mutable builder classes for immutable types.

17. Minimize Boxing and Unboxing

Note: Many of limitations discussed below are no longer applicable with the introduction of generics in C# 2.0. However, it makes sense to be aware of the issues discussed below to further help you minimize boxing and unboxing.

Value types are containers for data. They are not polymorphic types. On the other hand, the .NET Framework was designed with a single reference type, System.Object, at the root of the entire object hierarchy. These two goals are at odds. The .NET Framework uses boxing and unboxing to bridge the gap between these two goals. Boxing places a value type in an untyped reference object to allow the value type to be used where a reference type is expected. Unboxing extracts a copy of that value type from the box. Boxing and unboxing are necessary for you to use value types where the System.Object type is expected. But boxing and unboxing are always performance-robbing operations. Sometimes, when boxing and unboxing also create temporary copies of objects, it can lead to subtle bugs in your programs. Avoid boxing and unboxing when possible.

Boxing converts a value type to a reference type. A new reference object, the box, is allocated on the heap, and a copy of the value type is stored inside that reference object. The box contains the copy of the value type object and duplicates the interfaces implemented by the boxed value type. When you need to retrieve anything from the box, a copy of the value type gets created and returned. That's the key concept of boxing and unboxing: A copy of the object goes in the box, and another gets created whenever you access what's in the box.

Boxing and unboxing is that it happens automatically. The compiler generates the boxing and unboxing statements whenever you use a value type where a reference type, such as System.Object is expected. Even a simple statement such as this performs boxing:

Console.WriteLine("A few numbers:{0}, {1}, {2}", 25, 32, 50);

To avoid this particular penalty, you should convert your types to string instances yourself before you send them to WriteLine():

Console.WriteLine("A few numbers:{0}, {1}, {2}", 25.ToString(), 32.ToString(), 50.ToString());

First rule to avoid boxing: watch for implicit conversions to System.Object.

Another common case in which you might inadvertently substitute a value type for System.Object is when you place value types in .NET 1.x collections. This incarnation of the .NET Framework collections store references to System.Object instances. Anytime you add a value type to a collection, it goes in a box. Anytime you remove an object from a collection, it gets copied from the box. Taking an object out of the box always makes a copy. That introduces some subtle bugs in your application. See TestBoxingUnboxing1() and TestBoxingUnboxing2() methods below.

class Item17

{

    /// <summary>

    /// Illustrates boxing and unboxing. Person is a value type; it gets placed in a box

    /// before being stored in the ArrayList. That makes a copy. Then another copy gets

    /// made when you remove the Person object to access the Name property to change.

    /// All you did was change the copy. In fact, a third copy was made to call the

    /// ToString() function through the al[0] object.

    /// </summary>

    public void TestBoxingUnboxing1()

    {

        // Create and initialize a value type

        Person p = new Person();

        p.Name = "Old Name";

 

        // Use a Person object in a .NET 1.x collection. Boxing takes place automatically.

        // A COPY OF P IS PLACED IN A NEW OBJECT

        ArrayList al = new ArrayList();

        al.Add(p);

 

        // Try to change the name.

        Person pCopy = (Person)al[0];       // Unboxing creates a copy of Person at index 0

        pCopy.Name = "New Name";

 

        // Now write contents of array list

        Trace.WriteLine(al[0].ToString());      // Writes "Old Name"

    }

 

    /// <summary>

    /// The box reference type implements all the interfaces implemented by the original

    /// Person2 object. That means no copy is made, but you call the IPersonName.Name

    /// method on the box, which forwards the request to the boxed value type. Creating

    /// interfaces on your value types enables you to reach inside the box to change the

    /// value stored in the collection

    /// </summary>

    public void TestBoxingUnboxing2()

    {

        // Create and initialize a value type

        Person2 p = new Person2("Old Name");

 

        // Use a Person object in a .NET 1.x collection. Boxing takes place automatically.

        // A COPY OF P IS PLACED IN A NEW OBJECT

        ArrayList al = new ArrayList();

        al.Add(p);

 

        // Try to change the name. Use the interface, and not the type

        IPerson ip = (IPerson)al[0];       // Does not unbox

        ip.Name = "New Name";

 

        // Now write contents of array list

        Trace.WriteLine(al[0].ToString());      // Writes "New Name"

    }

}

 

namespace Item17NS

    struct Person

    {

        public string Name { get; set; }

        public override string ToString()

        {

            return Name;

        }

    }

 

    interface IPerson

    {

        string Name { get; set; }

    }

    struct Person2 : IPerson

    {

        public Person2(string name) : this()

        {

            Name = name;

        }

        #region IPerson Members

        public string Name { get; set; }

        #endregion

 

        public override string ToString()

        {

            return Name;

        }

    }

}

18. Implement the Standard Dispose Pattern

This item has also been discussed in under Programming / Data Structures &  Collections / System.Object chapter.

The following is a summary of how to implement IDispoable across a class hierarchy: The root base class in the class hierarchy should implement the IDisposable interface to free resources. This type should also add a finalizer as a defensive mechanism. Both of these routines delegate the work of freeing resources to a virtual method that derived classes can override for their own resource-management needs. The derived classes need override the virtual method only when the derived class must free its own resources and it must remember to call the base class version of the function.

The implementation of your IDisposable.Dispose() method is responsible for four tasks:

  1. Freeing all unmanaged resources.

  2. Freeing all managed resources (this includes unhooking events.)

  3. Setting a state flag to indicate that the object has been disposed. You need to check this state and throw ObjectDisposed exceptions in your public methods, if any get called after disposing of an object.

  4. Suppressing finalization. You call GC.SuppressFinalize(this) to accomplish this task.

When writing disposable objects, the most important recommendation for any method associated with disposal or cleanup is that you should be releasing resources only. Do not perform any other processing during a dispose method. You can introduce serious complications to object lifetimes by performing other processing in your Dispose or finalize methods. Finalizers should do nothing but clean up unmanaged resources. If a finalizer somehow makes an object reachable again, it has been resurrected. See class BadClass below

In a managed environment, you do not need to write a finalizer for every type you create; You implement IDisposable only for types that store unmanaged types or when your type contains members that implement IDisposable. Even if you need only the IDisposable interface, not a finalizer, implement the entire pattern. Otherwise, you limit your derived classes by complicating their implementation of the standard Dispose idiom.

class Item18

{

    /// <summary>

    /// The following code creates a derived class that allocates an unmanaged resource

    /// but does not explicitly call Dispose. Note in the output window how the finalization

    /// code takes care of freeing unmanaged resources. The following output is displayed

    /// when the program exits:

    ///

    ///     MyBaseResource.~MyBaseResource

    ///     MyDerivedResource.Dispose(False)

    ///     MyDerivedResource: Freeing unmanaged resources

    ///     MyBaseResource.Dispose(False)

    ///     MyBaseResource: Freeing unmanaged resources

    ///    

    /// </summary>

    public void TestDisposable1()

    {

        // Allocate a class that uses some unmanaged resource

        MyDerivedResource obD = new MyDerivedResource();

 

        // Do something with the unmanaged resource

        obD.foo();

    }

 

    /// <summary>

    /// The following code creates the same derived class that allocates an unmanaged resource

    /// and explicitly calls Dispose. Now note in the output window that the finalization code

    /// does not get executed. The following output is displayed when Dispose is called on obD:

    ///

    ///     MyBaseResource.Dispose()

    ///     MyDerivedResource.Dispose(True)

    ///     MyDerivedResource: Freeing managed resources

    ///     MyBaseResource.Dispose(True)

    ///     MyBaseResource: Freeing managed resources

    ///

    /// </summary>

    public void TestDisposable2()

    {

        // Allocate a class that uses some unmanaged resource

        MyDerivedResource obD = new MyDerivedResource();

 

        // Do something with the unmanaged resource

        obD.foo();

 

        // Dispose of managed and unmanaged resources

        obD.Dispose(); 

    }

}

 

namespace Item18NS

{

    // Cleanup design pattern for base classes

    public class MyBaseResource : IDisposable

    {

        /* Data members */

        bool bDisposed = false;     // Flag for already disposed

 

        /* Constructors/Destructors */

        // Use C# destructor for finalization code. This destructor will only run if

        // Dispose() was not called (Dispose if called, calls GC.SuppressFinalize)

        ~MyBaseResource()

        {

            Trace.WriteLine("MyBaseResource.~MyBaseResource");

            Dispose(false);

        }

 

        /* Public interface */

        public void DoSomething()

        {

            Trace.WriteLine("MyBaseResource.DoSomething");

        }

 

        /* Interface methods */

        // IDisposable.Dispose() implementation. This method should not be made override to

        // prevent derived classes from overriding

        public void Dispose()

        {

            Trace.WriteLine("MyBaseResource.Dispose()");

 

            // delegate to another method

            Dispose(true);

 

            // Then remove this object form the finalization queue

            GC.SuppressFinalize(this);

        }

 

        /* Implementation details */

        // Performs actual clean up. If bDisposing is true, Dispose has been called by the

        // user and both managed and unmanaged resources must be freed. If bDisposing is false,

        // this method has been called by the finalization code and it should release only

        // unmanaged resources

        protected virtual void Dispose(bool bDisposing)

        {

            Trace.WriteLine("MyBaseResource.Dispose(" + bDisposing + ")");

            if (bDisposed)

                return;

 

            Trace.Indent();

            if (bDisposing == true)

            {

                Trace.WriteLine("MyBaseResource: Freeing managed resources");

 

                // Dispose all managed and unmanaged resources.

                // TODO: free managed resources here

                // TODO: free un-managed resources here

            }

            else

            {

                Trace.WriteLine("MyBaseResource: Freeing unmanaged resources");

                // TODO: free un-managed resources here

            }

            bDisposed = true;

        }

    }

 

    // Cleanup design pattern for derived classes. Note that this class does not have

    // a Finalize method (destructor) because the base class's Finalize method will call

    // the derived class's Dispose(bDisposing). This class does not also have an

    // IDisposable.Dispose implementation because it inherits them from the base class

    public class MyDerivedResource : MyBaseResource

    {

        /* Data members */

        bool bDisposed = false;

 

        /* Public interface */

        public void foo()

        {

            Trace.WriteLine("MyDerivedResource.foo()");

            // Allocates an unmanaged resource

        }

 

        /* Inherited implementation detail */

        protected override void Dispose(bool bDisposing)

        {

            Trace.WriteLine("MyDerivedResource.Dispose(" + bDisposing + ")");

            if (bDisposed)

                return;

 

            Trace.Indent();

            try

            {

                if (bDisposing == true)

                {

                    Trace.WriteLine("MyDerivedResource: Freeing managed resources");

                    // Dispose all managed and unmanaged resources.

                    // TODO: free managed resources here

                    // TODO: free un-managed resources here

                }

                else

                {

                    Trace.WriteLine("MyDerivedResource: Freeing unmanaged resources");

                    // TODO: free un-managed resources here

                }

                bDisposed = true;

            }

            finally

            {

                // Now call the base class Dispose method

                base.Dispose(bDisposing);

            }

        }

    }

 

    /// <summary>

    /// Most important recommendation when implementing IDisposable: look very carefully

    /// at any code in a finalizer and, by extension, both Dispose methods. If that code

    /// is doing anything other than releasing resources, look again. Those actions likely

    /// will cause bugs in your program in the future. Remove those actions, and make sure

    /// that finalizers and Dispose() methods release resources and do nothing else.

    /// </summary>

    public class BadClass

    {

        // Store a reference to a global object:

        private readonly ArrayList _finalizedList;

        private string _msg;

 

        public BadClass(ArrayList badList, string msg)

        {

            // cache the reference:

            _finalizedList = badList;

            _msg = (string)msg.Clone();

        }

 

        ~BadClass()

        {

            // Add this object to the list. This object is reachable, no longer garbage.

            // It's Back!

            _finalizedList.Add(this);

        }

    } 

}

Design

19. Prefer Defining and Implementing Interfaces to Inheritance

Recall that abstract base classes provide a common ancestor for a class hierarchy. An interface describes one atomic piece of functionality that can be implemented by a type. A type that implements an interface must supply an implementation for expected methods. Abstract base classes provide a common abstraction for a set of related types.

Also recall that public inheritance means 'is a', while interface inheritance means 'behaves like'. In other words, base classes describe what an object is; interfaces describe one way in which it behaves.

Abstract base classes can supply some implementation for derived types, in addition to describing the common behavior. You can specify data members, concrete methods, implementation for virtual methods, properties, events, and indexers. A base class can provide implementation for some of the methods, thereby providing common implementation reuse. Any of the elements can be virtual, abstract, or nonvirtual. An abstract base class can provide an implementation for any concrete behavior; interfaces cannot. If you add a method to the base class, all derived classes are automatically and implicitly enhanced. In that sense, base classes provide a way to extend the behavior of several types efficiently over time: By adding and implementing  functionality in the base class, all derived classes immediately incorporate that behavior. Adding a member to an interface breaks all the classes that implement that interface. They will not contain the new method and will no longer compile. Each implementer must update that type to include the new member.

Choosing between an abstract base class and an interface is a question of how best to support your abstractions over time. Interfaces are fixed: You release an interface as a contract for a set of functionality that any type can implement. Base classes can be extended over time. Those extensions become part of every derived class.

An interface can be implemented by any number of unrelated types. Coding to interfaces provides greater flexibility to other developers than coding to base class types. That's important because of the single inheritance hierarchy that the .NET environment enforces. These two methods perform the same task:

public void PrintCollection( IEnumerable<int> collection )
{
    foreach( int n in collection )
        Console.WriteLine( "Collection contains {0}", n.ToString( ) );
}

public void PrintCollection( MyCollection<int> collection )
{
    foreach( int n in collection )
        Console.WriteLine( "Collection contains {0}", n.ToString( ) );
}

Coding the method using interfaces as its parameter types is far more generic and far easier to reuse. The second method is far less reusable. It cannot be used with List<T> and many other collection classes.

When your type exposes properties as class types, it exposes the entire interface to that class. Using interfaces, you can choose to expose only the methods and properties you want clients to use. The class used to implement the interface is an implementation detail that can change over time (see Item 23 Avoid Returning References to Internal Class Objects).

Furthermore, unrelated types can implement the same interface. Suppose you're building an application that manages employees, customers, and vendors. Those are unrelated, at least in terms of the class hierarchy. But they share some common functionality. They all have names, and you will likely display those names in Windows controls in your applications. See TestInterfaces() and more importantly, PrintContactInfo() methods below.

To summarize, base classes describe and implement common behaviors across related concrete types. Interfaces describe atomic pieces of functionality that unrelated concrete types can implement. Both have their place. Classes define the types you create. Interfaces describe the behavior of those types as pieces of functionality. Use class hierarchies to define related types. Expose functionality using interfaces implemented across those types.

class Item19

{

    /// <summary>

    /// In Item19NS_Bad namespace, the Employee, Customer, and Vendor classes do not

    /// share a common base class because they are unrelated. But they do share some

    /// properties. In Item19NS_Good namespace, these properties are factored out into

    /// an IContactInfo interface. This new interface simplifies programming tasks by

    /// letting you build common routines for unrelated types such as

    /// PrintContactInfo( IContactInfo) function below.

    /// </summary>

    public void TestInterfaces()

    {

        // Create a few unrelated types

        Item19NS_Good.Customer c = new Item19NS_Good.Customer() { FirstName = "A", LastName = "B" };

        Item19NS_Good.Employee e = new Item19NS_Good.Employee() { FirstName = "C", LastName = "D" };

        Item19NS_Good.Vendor v = new Item19NS_Good.Vendor() { FirstName = "E", LastName = "F" };

 

        // Because these unrelated types implement the same interface, we can

        // pass the interface as a parameter to a function that know how to

        // process that interface.

 

        PrintContactInfo(c as Item19NS_Good.IContactInfo);  // Output: FirstName: A. LastName: B

        PrintContactInfo(e as Item19NS_Good.IContactInfo);  // Output: FirstName: C. LastName: D

        PrintContactInfo(v as Item19NS_Good.IContactInfo);  // Output: FirstName: E. LastName: F

    }

 

    /// <summary>

    /// PrintContactInfo works for all entities that implement the IContactInfo interface.

    /// Customer, Employee, and Vendor all use the same routine—but only because we

    /// factored them into interfaces

    /// </summary>

    private void PrintContactInfo( Item19NS_Good.IContactInfo info)

    {

        Trace.WriteLine("FirstName: " + info.FirstName + ". LastName: " + info.LastName);

    }

}

 

namespace Item19NS_Bad

{

    class Customer

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

 

    class Employee

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

 

    class Venddor

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

}

 

namespace Item19NS_Good

{

    interface IContactInfo

    {

        string FirstName { get; set; }

        string LastName { get; set; }

    }

 

    class Customer : IContactInfo

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

 

    class Employee : IContactInfo

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

 

    class Vendor : IContactInfo

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

}

20. Distinguish Between Implementing Interfaces and Overriding Virtual Functions

At first glance, implementing an interface seems to be the same as overriding a virtual function. You provide a definition for a member that has been declared in another type. That first glance is very deceiving. Implementing an interface is very different from overriding a virtual function.

Derived classes cannot override an interface member implemented in a base class. Interfaces can be explicitly implemented, which hides them from a class's public interface. They are different concepts with different uses. But you can implement interfaces in such a manner that derived classes can modify your implementation. You just have to create hooks for derived classes. TestImg() method illustrates the fact that interface methods are not virtual.

But you often want to create interfaces, implement them in base classes, and modify the behavior in derived classes. You can. You've got two options. If you do not have access to the base class, you can re-implement the interface in the derived class. See class MyDerivedClass2 and method TestImg2() below.

The only way to fix this problem as shown in namespace Item20NS2 is to modify the base class, declaring that the interface methods should be virtual. MyDerivedClass and all classes derived from MyClass can declare their own methods for Message(). The overridden version will be called every time: through the MyDerivedClass reference, through the IMsg reference, and through the MyClass reference.

Implementing interfaces allows more options than creating and overriding virtual functions. You can create sealed implementations, virtual implementations, or abstract contracts for class hierarchies. You can decide exactly how and when derived classes can modify the default behavior for members of any interface your class implements. Interface methods are not virtual methods, but a separate contract.

class Item20

{

    /// <summary>

    /// Illustrates the fact that interface methods are not virtual.

    /// </summary>

    public void TestImsg()

    {

        // Create a derived class that has a new Message method which does

        // not override MyClass.Message, but rather hides it

        MyDerivedClass d = new MyDerivedClass();

        d.Message();            // Prints: MyDerivedClass.Message

 

        // Access the actual IMsg implemenation which is in the base class

        IMsg imsg = d as IMsg;

        imsg.Message();         // Prints: MyClass.Message

 

        MyClass b = d as MyClass;

        b.Message();            // Prints: MyClass.Message

    }

 

    public void TestImsg2()

    {

        // Create a derived class re-implements IMsg

        MyDerivedClass2 d = new MyDerivedClass2();

        d.Message();            // Prints: MyDerivedClass.Message

 

        // Access the actual IMsg implemenation which is now in the derived class

        // and not in the base calss

        IMsg imsg = d as IMsg;

        imsg.Message();         // Prints: MyDerivedClass.Message

 

        MyClass b = d as MyClass;

        b.Message();            // Prints: MyClass.Message

    }

 

    public void TestImsg3()

    {

        // Create a derived class that has a new Message method which does

        // not override MyClass.Message, but rather hides it

        Item20NS2.MyDerivedClass d = new Item20NS2.MyDerivedClass();

        d.Message();            // Prints: MyDerivedClass.Message

 

        // Access the actual IMsg implemenation which is in the base class

        Item20NS2.IMsg imsg = d as Item20NS2.IMsg;

        imsg.Message();         // Prints: MyDerivedClass.Message

 

        Item20NS2.MyClass b = d as Item20NS2.MyClass;

        b.Message();            // Prints: MyDerivedClass.Message

    }

}

 

namespace Item20NS

{

    // A Basic interface

    interface IMsg

    {

        void Message();

    }

 

    // MyClass implements IMsg

    public class MyClass : IMsg

    {

        public void Message()

        {

            Trace.WriteLine("MyClass.Message");

        }

    }

 

    // A derived class that derives from MyClass. Notice the use of the the new keyword in

    // the definition of the Message method. MyClass.Message() is not virtual. Derived classes

    // cannot provide an overridden version of Message. The MyDerived class creates a new

    // Message method, but that method does not override MyClass.Message; It hides it.

    // Furthermore, MyClass. Message is still available through the IMsg reference

    public class MyDerivedClass : MyClass

    {

        public new void Message()

        {

            Trace.WriteLine("MyDerivedClass.Message");

        }

    }

 

    /// <summary>

    ///  A derived class that derives from MyClass and re-implements IMsg. Note that you still

    ///  need the new keyword on the MyDerivedClass2.Message() method.

    /// </summary>

    public class MyDerivedClass2 : MyClass, IMsg

    {

        public new void Message()

        {

            Trace.WriteLine("MyDerivedClass.Message");

        }

    }

}

 

// Improves on the classes in Item20NS by declaring the interface methods as virtual

namespace Item20NS2

{

    // A Basic interface

    interface IMsg

    {

        void Message();

    }

 

    // MyClass implements IMsg

    public class MyClass : IMsg

    {

        public virtual void Message()

        {

            Trace.WriteLine("MyClass.Message");

        }

    }

 

    public class MyDerivedClass : MyClass

    {

        public override void Message()

        {

            Trace.WriteLine("MyDerivedClass.Message");

        }

    } 

}

21. Express Callbacks with Delegates

Callbacks are used to provide feedback from a server to a client asynchronously. Callbacks are expressed using delegates in the C# language. A delegate is an object that contains a reference to a method. That method can be either a static method or an instance method.

Delegates provide type-safe callback definitions. Although the most common use of delegates is events, that should not be the only time you use this language feature. Any time you need to configure the communication between classes and you desire less coupling than you get from interfaces, a delegate is the right choice. . Using the delegate, you can communicate with one or many client objects configured at runtime.

Multicast delegates wrap all the functions that have been added to the delegate in a single function call. Two caveats apply to this construct: It is not safe in the face of exceptions, and the return value will be the return value of the last function invocation. Inside a multicast delegate invocation, each target is called in succession. The delegate does not catch any exceptions. Therefore, any exception that the target  throws ends the delegate invocation chain.

You address both issues by invoking each delegate target yourself. Each delegate you create contains a list of delegates. To examine the chain yourself and call each one, iterate the invocation list yourself. See TestDelegates() method below.

Note that if we did not iterate the invocation list, then the value returned from invoking the delegate is the return value from the last function in the multicast chain. All other return values are ignored

class Item21

{

    private delegate bool ContinueProcessing(string strFileName);

    public void TestDelegates()

    {

        // Create list of file to encrypt and transmit via FTP

        List<string> lstFileNames = new List<string>() { "X.txt", "Y.txt", "Z.txt" };

 

        // Create a multi-cast delegate that will be used as a callback to check

        // processing on a file

        ContinueProcessing cp = new ContinueProcessing(CheckEncryptionStatus);

        cp += new ContinueProcessing(CheckValidContentStatus);

 

        // Prcoess files

        ProcessFiles(lstFileNames, cp);

    }

 

    /// <summary>

    /// Output:

    ///

    ///     Performing checks on file X.txt

    ///     CheckEncryptionStatus for X.txt

    ///     CheckTransmitStatus for X.txt

    ///     X.txt passes both checks. Processing...

    ///     Performing checks on file Y.txt

    ///     CheckEncryptionStatus for Y.txt

    ///     CheckTransmitStatus for Y.txt

    ///     Y.txt does not pass checks

    ///     Performing checks on file Z.txt

    ///     CheckEncryptionStatus for Z.txt

    ///     CheckTransmitStatus for Z.txt

    ///     Z.txt does not pass checks

    ///    

    /// </summary>

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

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

    private void ProcessFiles(List<string> lstFiles, ContinueProcessing cpDelegates)

    {

        bool bContinue = true;

        foreach (string file in lstFiles)

        {

            Trace.WriteLine("Performing checks on file " + file);

            // Check encryption and content before processing this file further

            foreach (ContinueProcessing cp in cpDelegates.GetInvocationList())

            {

                bContinue &= cp(file);

            }

 

            if (!bContinue)

            {

                Trace.WriteLine(file + " does not pass checks ");

                continue;

            }

 

            // Clear to further process this file ...

            Trace.WriteLine(file + " passes both checks. Processing..." );

        }

    }

 

    /// <summary>

    /// Callback method to check encryption status for a file

    /// </summary>

    private bool CheckEncryptionStatus(string file)

    {

        // Dummy processing

        Trace.WriteLine("CheckEncryptionStatus for " + file);

        if (file == "Y.txt")

            return false;

        else

            return true;

    }

 

    /// <summary>

    /// Callback method to check content status for a file

    /// </summary>

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

    private bool CheckValidContentStatus(string file)

    {

        // Dummy processing

        Trace.WriteLine("CheckTransmitStatus for " + file);

        if (file == "Z.txt")

            return false;

        else

            return true;

    } 

}

22. Define Outgoing Interfaces with Events

Events define the outgoing interface for your type. Events are built on delegates to provide type-safe function signatures for event handlers. You should use events when your type must communicate with multiple clients to inform them of actions in the system. Contrast this with callbacks (i.e., delegates) that are used to provide feedback from a server to a client asynchronously. See Logger class below for an example of how to properly design an event class

You define outgoing interfaces in classes with events: Any number of clients can attach handlers to the events and process them. Those clients need not be known at compile time. Events don't need subscribers for the system to function properly. Using events in C# decouples the sender and the possible receivers of notifications. The sender can be developed completely independently of any receivers. Events are the standard way to broadcast information about actions that your type has taken.

namespace Item22NS

{

    public class LoggerEventArgs : EventArgs

    {

        public readonly string Message;

        public readonly int Priority;

        public LoggerEventArgs(int p, string m)

        {

            Priority = p;

            Message = m;

        }

    }

 

    public class Logger

    {

        private static readonly Logger _instance = null;

        static Logger()

        {

            _instance = new Logger();

        }

 

        // Private constructor. This class cannot be instantiated

        private Logger() { /* Empty implementation */ }

 

        public Logger Singleton

        {

            get { return _instance; }

        }

 

        // Define the event

        public event EventHandler<LoggerEventArgs> Log;

 

        // add a message, and log it.

        public void AddMsg(int priority, string msg)

        {

            OnAddMsg(new LoggerEventArgs(priority, msg));

        }

 

        protected virtual void OnAddMsg(LoggerEventArgs args)

        {

            // Make a temporary copy of the event to avoid possibility of

            // a race condition if the last subscriber unsubscribes

            // immediately after the null check and before the event is raised.

            EventHandler<LoggerEventArgs> handler = Log;

 

            // Event will be null if there are no subscribers

            if (handler != null)

                handler(this, args);

        }

    }

}

23. Avoid Returning References to Internal Class Objects

A read-only property means that it is read-only and callers cannot modify it. Unfortunately, that's not always the way it works. If you create a property that returns a reference type, the caller can access any public member of that object, including those that modify the state of the property. For example:

public class MyBusinessObject
{
    // Read Only property providing access to a private data member:
    public MyReferenceType InternalData { get; }

    ...
}

MyReferenceType t = bizObj.Data;         // Access object's internal data
t.SomeImportantInternalList.Clear();     // Not intended, but allowed:

Clearly, you want to prevent this kind of behavior. You do not want users to access or modify the internal state of your objects without your knowledge. You've got four different strategies for protecting your internal data structures from unintended modifications: value types, immutable types, interfaces, and wrappers

  1. Value types are copied when clients access them through a property. Any changes to the copy retrieved by the clients of your class do not affect your object's internal state. Clients can change the copy as much as necessary to achieve their purpose.

  2. Immutable types, such as System.String, are also safe. You can return strings, or any immutable type, safely knowing that no client of your class can modify the string. Your internal state is safe.

  3. Define interfaces that allow clients to access a subset of your internal member's functionality (see Item 19 Prefer Defining and Implementing Interfaces to Inheritance). When you create your own classes, you can create sets of interfaces that support subsets of the functionality of your class. By exposing the functionality through those interfaces, you minimize the possibility that your internal data changes in ways you did not intend. Clients can access the internal object through the interface you supplied, which will not include the full functionality of the class.

  4. With wrapper objects, you can provide access to internal data but prevent the use of mutator methods. The following uses a ReadOnlyCollection<T> to expose an internal list rather then exposing the underlying List<T>:

    class Employees
    {
        private List<Employee> lst = new List<Employee>();

        // Return a read only view of the underlying collection
        private ReadOnlyCollection<Employee> EmployeeView
        {
            get {return new ReadOnlyCollection<Employee>(lst);
        }
    }

However, how do respond to changes in your data when you allow public clients to modify it? The general technique is to use the Observer pattern: Your class subscribes to events generated by your internal data structure. Event handlers validate changes or respond to those changes by updating other internal state.

In summary, exposing reference types through your public interface allows users of your object to modify its internals without going through the methods and properties you've defined. You need to modify your class's interfaces to take into account that you are exporting references rather than values. If you simply return internal data, you've given access to those contained members. Your clients can call any method that is available in your members. You limit that access by exposing private internal data using interfaces, or wrapper objects. When you do want your clients to modify your internal data elements, you should implement the Observer pattern so that your objects can validate changes or respond to them.

24. Prefer Declarative to Imperative Programming

Declarative programming can often be a simpler, more concise way to describe the behavior of a software program than imperative programming. Declarative programming means that the behavior of your program is defined using declarations instead of by writing instructions. You practice declarative programming using attributes in C#. You attach attributes to classes, properties, data members, or methods, and the .NET runtime adds behavior for you. This declarative approach is simpler to implement and easier to read and maintain.

If the predefined attributes don't fit your needs, you can create your own declarative programming constructs by defining custom attributes and using reflection. As an example, you can create an attribute and associated code to let users create types that define the default sort order using an attribute. See method TestSortingViaAttributes() below.

You could write different (and slightly simpler) versions of the sort algorithm for every type you created. The advantage to using declarative programming is that you can write one generic class and let a simple declaration create the behavior for each type. The key is that the behavior changes based on a single declaration, not based on any algorithm changes. The GenericComparer (defined below) works for any type decorated with the DefaultSort attribute (defined below). If you need sorting functionality only once or twice in your application, write the simpler routines. However, if you might need the same behavior for many tens of different types in your program, the generic algorithm and the declarative solution will save you time and energy in the long run.

Declarative programming is a powerful tool. When you can use attributes to declare your intent, you save the possibility of logic mistakes in multiple similar hand-coded algorithms. Declarative programming creates more readable, cleaner code. If you can use an attribute defined in the .NET Framework, do so. If not, consider the option of creating your own attribute definition so that you can use it to create the same behavior in the future.

class Item24

{

    public void TestSortingViaAttributes()

    {

        // Create a list of customers

        List<Customer> lstCustomers = new List<Customer>() { new Customer {Balance=100.0, Name="C"},

                                                             new Customer {Balance=200.0, Name="A"},

                                                             new Customer {Balance=300.0, Name="B"},

                                                             new Customer {Balance=400.0, Name="D"} };

 

        // Sort the list. The GenericComparer examines the DefaultSort attribute and sorts

        // lstCustomers based on the supplied property name

        lstCustomers.Sort(new GenericComparer<Customer>());

 

        // Display list contents

        //  C/100

        //  A/200

        //  B/300

        //  D/400

        Trace.WriteLine("Sort ascending");

        lstCustomers.ForEach( (Customer c) => Trace.WriteLine( c.ToString() ));

 

        // Sort descending

        lstCustomers.Sort(new GenericComparer<Customer>(true));

 

        // Display list contents

        //  D/400

        //  B/300

        //  A/200

        //  C/100

        Trace.WriteLine("Sort descending");

        lstCustomers.ForEach( (Customer c) => Trace.WriteLine( c.ToString()) );

    }

}

 

namespace Item24NS

{

    /// <summary>

    /// Defines a new attribute that indicates the field on which to sort

    /// a collection of objects.

    /// </summary>

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]

    class DefaultSortAttribute : System.Attribute

    {

        public string Name { get; set; }

        public DefaultSortAttribute(string name)

        {

            Name = name;

        }

    }

 

    /// <summary>

    /// DefaultSort attribute defines how a collection of Customer objects should

    /// be sorted

    /// </summary>

    [DefaultSort("Balance")]

    class Customer

    {

        public string Name { get; set; }

        public double Balance { get; set; }

 

        public override string ToString()

        {

            return Name + "/" + Balance;

        }

    }

 

    internal class GenericComparer<T> : IComparer<T>

    {

        private readonly PropertyDescriptor _sortProp;  // default property information

        private readonly bool _reverse = false;         // Ascending or descending.

 

        // Construct for a type

        public GenericComparer() :this(false) { /* Empty implementation */ }

 

        // Construct for a type and a direction

        public GenericComparer(bool reverse)

        {

            _reverse = reverse;

 

            // Get all DefaultSort attributes defined on this type

            object[] a = typeof(T).GetCustomAttributes( typeof(DefaultSortAttribute), false);

 

            // Get the PropertyDescriptor for that property:

            if (a.Length > 0)

            {

                DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;

                string name = sortName.Name;

 

                // Get list of properties defined on the current type. Search for the property

                // whose name matches the name given in DefaultSort attribute

                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));

                foreach (PropertyDescriptor p in props)

                {

                    if (p.Name == name)

                    {

                        // Found the default sort property:

                        _sortProp = p;

                        break;

                    }

                }

            }

        }

 

        #region IComparer<T> Members

        public int Compare(T left, T right)

        {

            // null is less than any real object:

            if ((left == null) && (right == null))

                return 0;

            if (left == null)

                return -1;

            if (right == null)

                return 1;

 

            if (_sortProp == null)

            {

                return 0;

            }

 

            // Get the sort property from each object:

            IComparable lField = _sortProp.GetValue(left) as IComparable;

            IComparable rField = _sortProp.GetValue(right) as IComparable;

            int rVal = 0;

            if (lField == null)

                if (rField == null)

                    return 0;

                else

                    return -1;

            rVal = lField.CompareTo(rField);

            return (_reverse) ? -rVal : rVal;               

        } 

        #endregion

    } 

}

25. Prefer Serializable Types

Persistence is a core feature of a type. It's one of those basic elements that no one notices until you neglect to support it. If your type does not support serialization properly, you create more work for all developers who intend to use your types as a member or base class. It's unlikely that clients could properly implement serialization for your types without access to private details in your types. If you don't supply serialization, it's difficult or impossible for users of your class to add it.

Instead, prefer adding serialization to your types when practical. It should be practical for all types that do not represent UI widgets, windows, or forms. .NET Serialization support is so simple that there is no reasonable excuse not to support it. In many cases, adding [Serializable] attribute is enough:

[Serializable]
public class MyType
{
    private string     _label;
    private int        _value;
    private OtherClass _object;
}

The [Serializable] attribute works here only if the OtherClass type supports .NET serialization. If OtherClass is not serializable, you get a runtime error and you have to write your own code to serialize MyType and the OtherClass object inside it. That's just not possible without extensive knowledge of the internals defined in OtherClass.

The [Serializable] attribute is the simplest technique to create serializable objects. Sometimes, you do not want to serialize all the members of an object; Some members might exist only to cache the result of a lengthy operation. Other members might hold on to runtime resources that are needed only for in-memory operations. Attach the [NonSerialized] attribute to any of the data members that should not be saved as part of the object state:

[Serializable]
public class MyType
{
    private string _label;      // Serialized

    [NonSerialized]
    private int _cachedValue;   // Not Serialized
    private OtherClass _object; // Serialized
}

[Nonserialized] members add a little more work for the class designer. Serialization APIs do not initialize nonserialized members during the deserialization process. None of the types' constructors is called, so the member initializers are not executed, either. When using the serializable attributes, nonserialized members get the default system-initialized value: 0 or null. If this default initialization is not right, you need to implement the IDeserializationCallback interface to initialize these nonserializable members. IDeserializationCallback contains only one method, OnDeserialization. The framework calls this method after the entire object graph has been deserialized. You use this method to initialize any nonserialized members in your object. Because the entire object graph has been read, you know that any function you might want to call on your object or any of its serialized members is safe. Unfortunately, it's not fool-proof. After the entire object graph has been read, the framework calls OnDeserialization on every object in the graph that supports the IDeserializationCallback interface. Any other objects in the object graph can call your object's public members when processing OnDeserialization. If they go first, your object's nonserialized members are null, or 0. Order is not guaranteed, so you must ensure that all your public methods handle the case in which nonserialized members have not been initialized.

Full examples of serialization can be found under .NET / Programming / Serialization chapter

26. Implement Ordering Relations with IComparable<T> and IComparer<T>

The .NET Framework defines two interfaces that describe ordering relationships for .NET types: IComparable<T> and IComparer<T> interfaces. IComparable<T>  defines the natural order for your types. A type implements IComparer<T> to describe alternative orderings.

Note that you should always use these generic interfaces rather than their non-generic equivalents. With the non-generic equivalents, you have to check the runtime type of the IComparable.CompareTo() argument. Also with the non- generic interfaces, proper arguments must be boxed and unboxed to provide the actual comparison. That's an extra runtime expense for each compare.

Ordering relations (IComparable<T> and IComparer<T>) and equality (Equals() or the operator== are distinct operations. You do not need to implement equality comparison to have an ordering relation. In fact, reference types commonly implement ordering based on the object contents, yet implement equality based on object identity. CompareTo() returns 0, even though Equals() returns false. That's perfectly legal. Equality and ordering relations are not necessarily the same.

IComparable<T> and IComparer<T> are the standard mechanisms for providing ordering relations for your types. IComparable<T> should be used for the most natural ordering. When you implement IComparable<T>, you should overload the comparison operators  (<, >, <=, and  >=) consistently with our IComparable<T> ordering. IComparer<T> can be used to provide alternative orderings or can be used when you need to provide ordering for a type that does not provide it for you.

class Item26

{

    public void TestIComparable()

    {

        // Create Customer objects that can be sorted

        Customer c1 = new Customer() { ID = 1, Name = "X" };

        Customer c2 = new Customer() { ID = 2, Name = "Y" };

        Customer c3 = new Customer() { ID = 3, Name = "Z" };

        List<Customer> lstCustomers = new List<Customer>() { c2, c3, c1 };

 

        // Sort customers with the default sort

        // X/1

        // Y/2

        // Z/3

        lstCustomers.Sort();

        lstCustomers.ForEach( (Customer c) => Trace.WriteLine( c.ToString() ));

 

        // Sort customers with a specialized sort

        // Z/3

        // Y/2

        // X/1

        lstCustomers.Sort(Customer.CustomerNameComparerDesc());

        lstCustomers.ForEach((Customer c) => Trace.WriteLine(c.ToString()));

 

        // Standard relational operators must be overridden before objects of type

        // Customer can be compared.

        bool b1 = c1 < c2;           

    }

}

 

namespace Item26NS

{

    /// <summary>

    /// A simple class that implements IComparable<T> interface.

    ///

    /// While not required, this class overrides the standard relational operators.

    /// Those should make use of the IComparable<T>.CompareTo() method as shown below.

    /// </summary>

    struct Customer : IComparable<Customer>

    {

        public string Name { get; set; }

        public int ID { get; set; }

 

        public override string ToString()

        {

            return Name + "/" + ID.ToString();

        }

        #region IComparable<Customer> Members

        public int CompareTo(Customer rhs)

        {

            // If Customer type was a class, then the following two statements

            // would be required

            //if (rhs == null) return -1;       // Check against a null input

            //if (rhs == this) return 0;        // Check against self-comparison

 

            return ID.CompareTo(rhs.ID);

        }

        #endregion

 

        #region IComparer<Customer> Members

        private class CustomerNameAsc : IComparer<Customer>

        {

            #region IComparer<Customer> Members

            public int Compare(Customer lhs, Customer rhs)

            {

                return lhs.CompareTo(rhs);

            }

            #endregion

        }

 

        private class CustomerNameDesc : IComparer<Customer>

        {

            #region IComparer<Customer> Members

            public int Compare(Customer lhs, Customer rhs)

            {

                return lhs.CompareTo(rhs) * -1;     // -1 to reverse comparison

            }

            #endregion

        }

 

        public static IComparer<Customer> CustomerNameComparerAsc()

        {

            return new Customer.CustomerNameAsc();

        }

 

        public static IComparer<Customer> CustomerNameComparerDesc()

        {

            return new Customer.CustomerNameDesc();

        }

 

        #endregion 

 

        // If operator < is implemented, then operator > must also be implemented.

        // Otherwise, error CS0216 is generated: The operator

        // 'Customer.operator <(Customer, Customer)' requires a matching operator '>'

        // to also be defined

        public static bool operator <(Customer lhs, Customer rhs)

        {

            return lhs.CompareTo(rhs) < 0;

        }

 

        public static bool operator >(Customer lhs, Customer rhs)

        {

            return lhs.CompareTo(rhs) > 0;

        }

 

        public static bool operator <= (Customer lhs, Customer rhs)

        {

            return lhs.CompareTo( rhs) <= 0;

        }

 

        public static bool operator >=(Customer lhs, Customer rhs)

        {

            return lhs.CompareTo(rhs) >= 0;

        }

    }

}

27. Avoid ICloneable

ICloneable sounds like a good idea: You implement the ICloneable interface for types that support copies. If you don't want to support copies, don't implement it. But your type does not live in a vacuum. Your decision to support ICloneable affects derived types as well. Once a type supports ICloneable, all its derived types must do the same. All its member types must also support ICloneable or have some other mechanism to create a copy. Finally, supporting deep copies can be very problematic when you create designs that contain webs of objects. ICloneable finesses this problem in its official definition: It supports either a deep or a shallow copy. A shallow copy creates a new object that contains copies of all member variables. If those member variables are reference types, the new object refers to the same object that the original does. A deep copy creates a new object that copies all member variables as well. All reference types are cloned recursively in the copy. In built-in types, such as integers, the deep and shallow copies produce the same results. Which one does a type support? That depends on the type. But mixing shallow and deep copies in the same object causes quite a few inconsistencies. When you go wading into the ICloneable waters, it can be hard to escape. Most often, avoiding ICloneable altogether makes a simpler class. It's easier to use, and it's easier to implement.

Any value type that contains only built-in types as members does not need to support ICloneable; a simple assignment copies all the values of the struct more efficiently than Clone(). The Clone() method must box its return so that it can be coerced into an Object reference. The caller must perform another cast to extract the value from the box. You've got enough to do. Don't write a Clone() function that replicates assignment.

What about value types that contain reference types? The most obvious case is a value type that contains a string:

public struct ErrorMessage
{
    private int errCode;
    private int details;
    private string msg;
}

string is a special case because this class is immutable. If you assign an error message object, both error message objects refer to the same string. This does not cause any of the problems that might happen with a general reference type. If you change the msg variable through either reference, you create a new string object. Therefore, do not implement ICloneable on a value type (struct) even if it contains strings.

The general case of creating a struct that contains arbitrary reference variables is more complicated. It's also far more rare. The built-in assignment for the struct creates a shallow copy of the container reference variable, with both structs referring to the same object. To create a deep copy, you need to clone the contained reference type, and you need to know that the reference type supported a deep copy with its Clone() method. In either way, you don't add support for ICloneable to a value type; the assignment operator creates a new copy of any value type. Therefore, Do not implement ICloneable on a value type (struct) that contains arbitrary reference variables.

Reference types should support the ICloneable interface to indicate that they support either shallow or deep copying. You should add support for ICloneable judiciously; When you implement ICloneable, you force all derived classes to implement ICloneable as well. See TestICloneable1() method below.

When an entire hierarchy must implement ICloneable, create an abstract Clone() method and force all derived classes to implement it. In those cases, you define a protected copy constructors on the base to allow the derived class to create copies of the base members. See method TestICloneable2() below.

ICloneable does have its use, but it is the exception rather than rule. You should never add support for ICloneable to value types; use the assignment operation instead. You should add support for ICloneable to leaf classes when a copy operation is truly necessary for the type. Base classes that are likely to be used where ICloneable will be supported should create a protected copy constructor. In all other cases, avoid ICloneable.

class Item27

{

    /// <summary>

    /// Attempts to clone a derived object that does not implement ICloneable but whose

    /// base does implement ICloneable. In the code below, dCloned is NULL. MyDerived

    /// class does inherit ICloneable.Clone() from MyBase, but that implementation is

    /// not correct for MyDerived: It only clones the base type. MyBase.Clone() creates

    /// a MyBase object, not a MyDerived object. Therefore, when you implement ICloneable,

    /// you force all derived classes to implement it as well. To support cloning, derived

    /// classes can add only member variables that are value types or reference types that

    /// implement ICloneable.

    /// </summary>

    public void TestICloneable1()

    {

        // Create and initiailze a MyDerived object

        MyDerived d = new MyDerived();

        d.Name = "X";

        d.IDs = new int[] { 1, 2, 3, 4, 5 };

        d.Salries = new double[] { 100.0, 200.0, 300.0 };

 

        // Now clone object d. d is NULL!

        MyDerived dCloned = d.Clone() as MyDerived;          

    }

 

    public void TestICloneable2()

    {

        // Create and initiailze a Derived object

        Derived d = new Derived();

        d.Name = "X";

        d.IDs = new int[] { 1, 2, 3, 4, 5 };

        d.Salries = new double[] { 100.0, 200.0, 300.0 };

 

        // Clone object d. d is not null and cloned properly

        Derived dCloned = d.Clone() as Derived;

    }

}

 

namespace Item27NS

{

    public class MyBase : ICloneable

    { 

        public string Name { get; set; }

        public int[] IDs { get; set; }

 

        #region ICloneable Members

        public object Clone()

        {

            // Create and initialize a clonet

            MyBase clone = new MyBase();

            clone.Name = this.Name;               

            clone.IDs = this.IDs.ToArray();

 

            return clone;

        }

        #endregion

    }

 

    public class MyDerived : MyBase

    {

        public double[] Salries {get; set;}

    }

}

 

namespace Item27NS2

{

    class BaseType

    {

        public string Name { get; set; }

        public int[] IDs { get; set; }

 

        public BaseType() {/* Empty Implementation */ }

 

        // Protected copy constructor used by derived types to clone

        protected BaseType(BaseType src)

        {

            Name = src.Name;

            IDs = src.IDs.Clone() as int[];

        }

    }

 

    sealed class Derived : BaseType, ICloneable

    {

        public double[] Salries { get; set; }

 

        public Derived() {/* Empty Implementation */ }

 

        // Construct a copy using the base class copy ctor

        private Derived(Derived src) : base(src)

        {

            Salries = src.Salries.Clone() as double[];

        }

 

        public object Clone()

        {

            Derived rVal = new Derived(this);

            return rVal;

        }

    }

}

28. Avoid Conversion Operators

Conversion operators introduce a kind of substitutability between classes. Substitutability means that one class can be substituted for another. When you define a conversion operator for your type, you tell the compiler that your type may be substituted for the target type. These substitutions often result in subtle errors because your type probably isn't a perfect substitute for the target type. If you want to convert another type into your type, use a constructor. This more clearly reflects the action of creating a new object. Conversion operators can introduce hard-to-find problems in your code.

Assume you have a hierarchy where Circle, Ellipse and Square derive from a Shape base class. However, you realize that every Circle could be an Ellipse. In addition, some ellipses could be substituted for circles. That leads you to add two conversion operators. Every Circle is an Ellipse, so you add an implicit  conversion to create a new Ellipse from a Circle. An implicit conversion operator will be called whenever one type needs to be converted to another type. By contrast, an explicit conversion will be called only when the programmer puts a cast operator in the source code.

public class Circle : Shape
{
    private PointF _center;
    private float _radius;

    public Circle(PointF c, float r)
    {
        _center = c;
        _radius = r;
    }

    static public implicit operator Ellipse(Circle c)
    {
        return new Ellipse(c._center, c._center, c._radius, c._radius);
    }
}

Conversion from Circle to Ellipse happens automatically in the following function:

public double ComputeArea( Ellipse e)
{
    // Compute and return area of the ellipse
}

// call it:
Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f );
ComputeArea( c );

This sample shows substitutability: A Circle object has been substituted for an Ellipse object. The ComputeArea function above works even with the substitution. But examine this function:

public void Flatten( Ellipse e )
{
    e.R1 /= 2;
    e.R2 *= 2;
}

// call it using a circle:
Circle c = new Circle( new PointF ( 3.0f, 0 ), 5.0f );
Flatten( c );

This won't work. The Flatten() method takes an ellipse as an argument. The compiler convert a Circle to an Ellipse. You've created an implicit conversion that does that. Your conversion gets called, and the Flatten() function receives as its parameter the Ellipse created by your implicit conversion. This temporary object is modified by the Flatten() function and immediately becomes garbage. The side effects expected from your Flatten() function occur, but only on a temporary object. The end result is that nothing happens to the Circle object, c.

Changing the conversion from implicit to explicit only forces users to add a cast to the call:

Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f );
Flatten( ( Ellipse ) c );


The original problem remains. You just forced your users to add a cast to cause the problem. You still create a temporary object, flatten the temporary object, and throw it away. The Circle object c  is not modified at all. Instead, if you create a constructor to convert the Circle to an Ellipse, the actions are clearer:

Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f );
Flatten ( new Ellipse( c ));

Most programmers would see the previous two lines and immediately realize that any modifications to the Ellipse object passed to Flatten() are lost. They would fix the problem by keeping track of the new object:

Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f );
//Work with the circle.
// ...

// Convert to an ellipse.
Ellipse e = new Ellipse( c );
Flatten( e );

Conversion operators introduce a form of substitutability that causes problems in your code. You're indicating that, in all cases, users can reasonably expect that another class can be used in place of the one you created. When this substituted object is accessed, you cause clients to work with temporary objects or internal fields in place of the class you created. You then modify temporary objects and discard the results. These subtle bugs are hard to find because the compiler generates code to convert these objects. Avoid conversion operators.

29. Use the new Modifier Only When Base Class Updates Mandate It

You use the new modifier on a class member to redefine a nonvirtual member inherited from a base class. Redefining nonvirtual methods creates ambiguous behavior. Consider the following block of code:

object c = MakeObject();

// Call through MyClass reference:
MyClass cl = c as MyClass;
cl.MagicMethod( );

// Call through MyOtherClass reference:
MyOtherClass cl2 = c as MyOtherClass;
cl2.MagicMethod( );

When the new modifier is involved, that just isn't the case:

public class MyClass
{
    public void MagicMethod( )
    {
        // ...
   
}
}

public class MyOtherClass : MyClass
{
    // Redefine MagicMethod for this class.
   
public new void MagicMethod( )
    {
        // ...
   
}
}


If you call the same function on the same object, you expect the same code to execute. The fact that changing the reference, the label, that you use to call the function changes the behavior feels very wrong. It's inconsistent. A MyOtherClass object behaves differently in response to how you refer to it. The new modifier does not make a nonvirtual method into a virtual method after the fact. Instead, it lets you add a different method in your class's naming scope.

There is one time, and one time only, when you want to use the new modifier: You add new to incorporate a new version of a base class that contains a method name that you already use. The new modifier handles the case in which an upgrade to a base class now collides with a member that you previously declared in your derived class.

The new modifier must be used with caution. If you apply it indiscriminately, you create ambiguous method calls in your objects. It's for the special case in which upgrades in your base class cause collisions in your derived class. Even in that situation, think carefully before using it. Most importantly, don't use it in any other situations.

Binary Components

30. Prefer CLS-Compliant Assemblies

The .NET environment is language agnostic: Developers can incorporate components written in different .NET languages without limitations. In practice, it's almost true. You must create assemblies that are compliant with the Common Language Subsystem (CLS) to guarantee that developers writing programs in other languages can use your components.

To create a CLS-compliant assembly, you must create an assembly whose public interface is limited to those features in the CLS specification. Then any language supporting the CLS specification must be capable of using the component.

To create a CLS-compliant assembly, you must follow two rules. First, the type of all parameters and return values from public and protected members must be CLS compliant. Second, any non-CLS-compliant public or protected member must have a CLS-compliant synonym.

The first rule is simple to follow: You can have it enforced by the compiler. Add the [CLSCompliant] attribute to your assembly:

[assembly: CLSCompliant(true)]

After turning on CLS compliance, these two definitions won't compile because unsigned integers are not compliant with CLS:

//Not CLS Compliant, returns unsigned int:
public UInt32 Foo( )
{
    return _foo;
}

// Not CLS compliant, parameter is an unsigned int.
public void Foo2( UInt32 parm )
{
}

Remember that creating a CLS-compliant assembly affects only items that can be seen outside of the current assembly. Foo and Foo2 generate CLS compliance errors when declared either public or protected. However, if Foo and Foo2 were internal, or private, they could be included in a CLS-compliant assembly.

31. Prefer Small, Simple Functions

Sometimes what worked in our previous environment is counterproductive in the .NET environment. This is very true when you try to hand-optimize algorithms for the C# compiler. Your actions often prevent the JIT compiler from more effective optimizations. You're better off writing the clearest code you can create. Let the JIT compiler do the rest. One of the most common examples of premature optimizations causing problems is when you create longer and more complicated functions in the hopes of avoiding function calls.

The .NET runtime invokes the JIT compiler to translate the IL generated by the C# compiler into machine code. The CLR invokes the JITer on a function-by-function basis. Functions that do not ever get called do not get JITed. You can minimize the amount of extraneous code that gets JITed by factoring code into more, smaller functions rather than fewer larger functions.

Smaller and simpler functions make it easier for the JIT compiler to support enregistration. Enregistration is the process of selecting which local variables can be stored in registers rather than on the stack. Creating fewer local variables gives the JIT compiler a better chance to find the best candidates for enregistration.

The JIT compiler also makes decisions about inlining methods. Inlining means to substitute the body of a function for the function call. But remember that even small functions that are virtual or that contain try/catch blocks cannot be inlined.

Remember that translating your C# code into machine-executable code is a two-step process. The C# compiler generates IL that gets delivered in assemblies. The JIT compiler generates machine code for each method (or group of methods, when inlining is involved), as needed. Small functions make it much easier for the JIT compiler to amortize that cost. Small functions are also more likely to be candidates for inlining.

32. Prefer Smaller, Cohesive Assemblies

Smaller and cohesive assemblies means that you should build assemblies that are the right size and contain a small number of public types. Many developers put everything but the kitchen sink in one assembly. That makes it hard to reuse components and harder to update parts of a system. Many smaller assemblies make it easier to use your classes as binary components.

Cohesion is the degree to which the responsibilities of a single component form a meaningful unit. Cohesive components can be described in a single simple sentence.  For example, the System.Collections assembly 'provides data structures for storing sets of related objects' and the System.Windows.Forms assembly 'provides classes that model Windows controls.' You should be able to describe your own assemblies in the same fashion using one simple sentence.

Your goal is to create the best-sized package for the functionality you are delivering in your component. This goal is easier to achieve with cohesive components: Each component should have one responsibility.

Smaller assemblies also let you amortize the cost of application startup. The larger an assembly is, the more work the CPU does to load the assembly and convert the necessary IL into machine instructions. Only the routines called at startup are JITed, but the entire assembly gets loaded and the CLR creates stubs for every method in the assembly.

33. Limit Visibility of Your Types

Not every type you create needs to be public. You should give each type the least visibility necessary to accomplish your purpose. Expose only what needs to be exposed. Try implementing public interfaces with less visible classes.

Exposing your functionality using interfaces enables you to more easily create internal classes without limiting their usefulness outside of the assembly (see Item 19 Prefer Defining and Implementing Interfaces to Inheritance). Does the type need to be public, or is an aggregation of interfaces a better way to describe its functionality? Internal classes allow you to replace the class with a different version, as long as it implements the same interfaces. As an example, consider Item33NS_Bad.PhoneValidator class that validates phone numbers. Assume now you need to use an international version in one installation. Rather than stick the extra functionality in this one class, you're better off reducing the coupling between the different items. You create an interface to validate any phone number, and you change the existing phone validator to implement that interface, and make it an internal class. See Item33NS_Good members for full example.

Outside the assembly, only the IPhoneValidator interface is visible. The classes, which are specific for different regions in the world, are visible only inside the assembly. You can add different validation classes for different regions without disturbing any other assemblies in the system. By limiting the scope of the classes, you have limited the code you need to change to update and extend the entire system.

class Item33

{

    public void TestPublicTypeVisiblilty()

    {

        // Bad style

        Item33NS_Bad.PhoneValidator pv = new Item33NS_Bad.PhoneValidator();

        pv.ValidateNumber("0919 555 66666");

 

        // Good style

        IPhoneValidator ipv = PhoneConverterFactory.CreatePhoneConverter(EnumPhoneConverter.US);

        ipv.ValidateNumber("0044 0207 555 6666");

    } 

}

 

namespace Item33NS_Bad

{

    public class PhoneValidator

    {

        public bool ValidateNumber(string no)

        {

            // perform validation. Check for valid area code, exchange.

            return true;

        }

    }

}

 

namespace Item33NS_Good

{

    public interface IPhoneValidator

    {

        bool ValidateNumber(string no);

    }

 

    // Note use of 'internal' access modifier

    internal class USPhoneValidator : IPhoneValidator

    {

        public bool ValidateNumber(string no)

        {

            // perform validation. Check for valid area code, exchange.

            return true;

        }

    }

 

    // Note use of 'internal' access modifier

    internal class InternationalPhoneValidator : IPhoneValidator

    {

        public bool ValidateNumber(string no)

        {

            // perform validation. Check international code.

            return true;

        }

    }

  

    public enum EnumPhoneConverter { US, INTL }

 

    // Factory class to create proper phone validator

    public class PhoneConverterFactory

    {

        public static IPhoneValidator CreatePhoneConverter(EnumPhoneConverter ePC )

        {

            switch (ePC)

            {

                case EnumPhoneConverter.INTL:

                    return new InternationalPhoneValidator() as IPhoneValidator;

                case EnumPhoneConverter.US:

                    return new USPhoneValidator() as IPhoneValidator;

                default:

                    throw new InvalidOperationException("Invalid option");

            }

        }

    }

}

34. Create Large-Grain Remoting/WCF APIs

The cost and inconvenience of a communication protocol dictates how you should use the medium. Whether you use WCF or a web service, you must remember that the most expensive part of the operation comes when you transfer objects between distant machines. You must stop creating remote APIs that are simply a repackaging of the same local interfaces that you use. It works, but it reeks of inefficiency. Your application waits for the network each time you make a round trip to pass a new piece of information through the pipe. The more granular the API is, the higher percentage of time your application spends waiting for data to return from the server.

Instead, create WCF interfaces based on serializing documents or sets of objects between client and server. Your remote communications should work like the order form you fax to the catalog company: The client machine should be capable of working for extended periods of time without contacting the server. Then, when all the information to complete the transaction is filled in, the client can send the entire document to the server. The server's responses work the same way: When information gets sent from the server to the client, the client receives all the information necessary to complete all the tasks at hand.

For example, in an order processing system, you may have a Customer class to represent customers. When it comes to client-server interactions, you would create a local Customer object and transfer the Customer to the server after all the fields have been set:

Customer customer = new Customer () { Name="X", ShippingAddress="Some Address" };
proxyServer.AddCustomer( customer );

The customer example illustrates an obvious and simple example: transfer entire objects back and forth between client and server. But to write efficient programs, you need to extend that simple example to include the right set of related objects. Making remote invocations to set a single property of an object is too small of a granularity. But one customer might not be the right granularity for transactions between the client and server, either.

Imagine that it is a major catalog ordering house with 1 Million customers, and that each customer has, on average, 15 orders in the last year. Your design task is to determine the most efficient set of objects to transfer between client machines and the server.

You can begin by eliminating some obvious choices. Retrieving every customer and every order is clearly prohibitive: 1 million customers and 15 million order records are just too much data to bring to each client. The obvious choice is to retrieve one customer, with all orders that have been placed by that customer. The server method would be something like this:

public OrderData FindOrders( string customerName ) { ... }

Or is that right? Orders that have been shipped and received by the customer are almost certainly not needed at the client machine. A better answer is to retrieve only the open orders for the requested customer. The server method would change to something like this:

public OrderData FindOpenOrders( string customerName )

In general, client/server interactions are best optimized, by using the following pattern:

  1. On startup, retrieve all relevant data

  2. When changes are done, update the server.

  3. Server updates the database and uses a publisher/subscriber module to publish all changed data to connected clients.

The end result, is that clients only need to connect to the server on initial startup and when they need to update data.

But how can we further limit the size of each transaction without increasing the number of transactions or the latency of the server's response. Here you need some statistics to help make you intelligent assumptions with respect to system usage. For example, you track some statistics and find that if customers go six months without ordering, they are very unlikely to order again. So you stop retrieving those customers and their orders at the beginning of the day. That shrinks the size of the initial transaction. You also find that any customer who calls shortly after placing an order is usually inquiring about the last order. So you modify the list of orders sent down to the client to include only the last order rather than all orders.

To summarize, you want to minimize both the frequency and the size of the transactions sent between machines. Those two goals are at odds, and you need to make trade-offs between them. You should end up close to the center of the two extremes, but err toward the side of fewer, larger transactions.

Framework

35. Prefer Overrides to Event Handlers

Many .NET classes provide two different ways to handle events from the system. You can attach an event handler, or you can override a virtual function in the base class. Why provide two ways of doing the same thing? Because different situations call for different methods. Inside derived classes, you should always override the virtual function. Limit your use of the event handlers to responding to events in unrelated objects.

The following illustrates the two approaches for handling the MouseDown event:

public class MyForm : Form
{
    protected override void OnMouseDown(MouseEventArgs e)
    {
        try
       
{
            HandleMouseDown( e );
        }
        catch ( Exception e1 )
        {
            // add specific error handling here.
        }
        // Almost always call base class to let other event handlers process message.
        // Users of your class expect it.
        base.OnMouseDown( e );
    }
}

Or, you could attach an event handler:

public class MyForm : Form
{
    public MyForm( )
    {
        this.MouseDown += new MouseEventHandler( this.MouseDownHandler );
    }

    private void MouseDownHandler( object sender, MouseEventArgs e )
    {
        try
        {
            HandleMouseDown( e );
        }
        catch ( Exception e1 )
        {
            // add specific error handling here.
        }
    }
}

The first method is preferred. If an event handler throws an exception, no other handlers in the chain for that event are called (see item 21 Express Callbacks with Delegates). By overriding the protected virtual function, your handler gets called first. The base class version of the virtual function is responsible for calling any event handlers attached to the particular event. That means that if you want the event handlers called (and you almost always do), you must call the base class. In some rare cases, you will want to replace the default behavior instead of calling the base class version so that none of the event handlers gets called. You can't guarantee that all the event handlers will be called because some ill-formed event handler might throw an exception, but you can guarantee that your derived class's behavior is correct.

Using the override is more efficient than attaching the event handler. Event-handling mechanism takes more work for the processor because it must examine the event to see if any event handlers have been attached. If so, it must iterate the entire invocation list. Each method in the event invocation list must be called. Determining whether there are event handlers and iterating each at runtime takes more execution time than invoking one virtual function.

As mentioned above, the overrides are for derived classes. Every other class must use the event mechanism. For example, you often add a button click handler in a form. The event is generated by the button, but the form object handles the event. You could define a custom button and override the click handler in that class, but that's too much work to handle one event. Somehow, your custom button must communicate to the form that the button was clicked. The obvious way to handle that is to create an event. It would be simpler to just attach the form's event handler to the form in the first place. That's why the .NET Framework designers put those events in the forms in the first place.

Another reason for the event mechanism is that events are wired up at runtime. You have more flexibility using events. You can wire up different event handlers, depending on the circumstances of the program. Finally, with events, you can hook up multiple event handlers to the same event. For example, in a drawing program you might have multiple event handlers hooked up on the MouseDown event. The first would perform the particular action. The second might update the status bar or update the accessibility of different commands. Multiple actions can take place in response to the same event.

36, Leverage .NET Runtime Diagnostics

Tracing is the most important debugging technique:

System.Diagnostics namespace contains two classes for tracing System.Diagnostics.Debug and System.Diagnostics.Trace. Use the first only if you want the traces to be in Debug builds. Use the second, if you want trace statements in both Release and Debug builds. When a message is generated through a call to one of the Write() methods, the message is passed to listeners registered for the current application domain. It is the responsibility of those listeners to decide what to do with those messages. When the application domain is created, an instance of the DefaultTraceListener class is added to the list of registered listeners. This list is held in the static property Debug.Listeners which allows you to manage the list of listeners. If you do not want the default listener, you can remove it and add your own. Every listener class derives from the base TraceListener class.

In addition to the default trace listener, DefaultTraceListener, the Framework provides two other listener classes, TextWriterTraceListener and EventLogTraceListener, which write to a log file and the event log, respectively:

// C#
Debug.Listeners.Add( new TextWriterTraceListener(Console.out) );    // send output to the console

An alternative to the Debug class is the Trace class. Trace has the same methods and properties as Debug, but the code is only called if you define the TRACE symbol. However, note that release code should not be compiled with TRACE defined. If you find that a process on your machine has been compiled with TRACE defined, you can turn off this facility (and route the trace messages to a log file) through a configuration file:

<configuration>
    <system.diagnostics>
        <assert assertuienabled="false" logfilename="asserts.log"/>
    </system.diagnostics>
</configuration>

One final way of diagnosing problems is to define a global variables or switch that can turn tracing on or off. The best way to control this variable is through the configuration file. The configuration file can contain many such  switches, and diagnostic code can read these swtich(es) and act according. For example, one switch might enable/disable tracing only for basic information, while another might enable/disable tracing for extensive debugging information:

// Configuraiton file
<configuration>
    <system.diagnostics>
        <switches>
            <add name="BasicInfo" value="1"/>
            <add name="DetailedInfo" value="0"/>
       <switches>
    </system.diagnostics>
</configuration>

// C#
public class test
{
    public void f()
    {
        BooleanSwitch bBasic    = new BooleanSwitch("BasicInfo", "Display basic information");
        BooleanSwitch bDetailed = new BooleanSwitch("DetailedInfo", "Display detailed information");

        if (bBasic.Enabled)
            // Write only basic tracing information
        else if(bDetailed.Enabled)
            // Write detailed tracing information
        else
            // Do not write tracing statements

        ...
    }
}

The System.Diagnostics namespace also contains a switch called TraceSwitch. This class has a Level property that is one of the TraceLevel enum values (Error, Warning, Verbose, Info). In your code, you create and check a TraceSwitch object to determine the amount of information to display.

39. Use .NET Validation

The .NET Framework has extensive capabilities that you can use to minimize the amount of code you need to write, yet still validate every piece of data that your users give you. For Windows Forms validation you need to write an event handler for the System.Windows.Forms.Control.Validating event. Or, if you are creating your own custom control, override the OnValidating method (see Item 35 Prefer Overrides to Event Handlers). A standard form for a validation event handler follows:

private void textBoxName_Validating( object sender, System.ComponentModel.CancelEventArgs e )
{
    string error = null;

    // Perform your test
    if ( textBoxName.Text.Length == 0 )
    {
        // If the test fails, set the error string and cancel the validation event.
        error = "Please enter a name";
        e.Cancel = true;
    }
    // Update the state of an error provider with the correct error text. Set to null for no error.
    this.errorProviderAll.SetError( textBoxName, error );
}

You have a few more small tasks to make sure that no invalid input sneaks through. Every control contains a CausesValidation property. This property determines whether the control participates in validation. In general, you should leave it true for all of your controls, except for the Cancel button. If you forget, the user must create valid input to cancel from your dialog box. The second small task is to add an OK handler to force validation of all controls. Validation happens only when a user visits and leaves a control. If the user opens a form and immediately presses OK, none of your validation code executes. To fix that, you add an OK button handler to walk through all your controls and force them to validate. The following two routines show you how to do this correctly. The recursive routines handle those controls that are also containers for other controls: tab pages, group boxes, and panels:

private void buttonOK_Click( object sender, System.EventArgs e )
{
    // Validate everyone: Here, this.DialogResult will be set to DialogResult.OK
    ValidateAllChildren( this );
}

private void ValidateAllChildren( Control parent )
{
    // If validation already failed, stop checking.
    if( this.DialogResult == DialogResult.None )
        return;

    // For every control
    foreach( Control c in parent.Controls )
    {
        // Give it focus
        c.Focus( );

        // Try and validate:
        if (!this.Validate( ))
        {
            // when invalid, don't let the dialog close:
            this.DialogResult = DialogResult.None;
            return;
        }
        // Validate children
        ValidateAllChildren( c );
    }
}

40. Match Your Collection to Your Needs

Which collection is best?, well it depends. Different collections have different performance characteristics and are optimized for different actions. The .NET Framework supports many familiar collections: lists, arrays, queue, stack, and others. C# supports multidimensional arrays, which have performance characteristics that differ from either single-dimensional arrays or jagged arrays. The framework also includes many specialized collections; look through those before you build your own.

To pick the right collection for your proposed use, you need to consider the actions you'll most often perform on that collection. To produce a resilient program, you'll rely in the interfaces that are implemented by the collection classes so that you can substitute a different collection when you find that your assumptions about the usage of the collection were incorrect (see Item 19).

The .NET Framework has three different kinds of collections: arrays, array-like collections, and hash-based containers. Arrays are the simplest and generally the fastest, so let's start there. This is the collection type you'll use most often.

class Item40

{

    /// <summary>

    /// Using a 1-D array

    /// </summary>

    public void Test1DArray()

    {

        // Create and initialize an array using object

        int[] aInts = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

 

        // You can iterate an array using foreach

        foreach (int n in aInts)

            Trace.WriteLine(n.ToString());

 

        // Or you can iterate an array using an enumerator

        IEnumerator it = aInts.GetEnumerator();

        while (it.MoveNext())

            Trace.WriteLine( Convert.ToString(it.Current));

    }

 

    /// <summary>

    /// With jagged arrays, each element is an array with a size that is typically

    /// different than other elements. Use jagged arrays when you need to create

    /// differently sized arrays of arrays. Initializing the jagged array requires

    /// multiple initialization statements. In the example below, you need three

    /// statements. Larger arrays or arrays with more dimensions require more extensive

    /// initialization code. You must write code by hand.

    /// </summary>

    public void TestJaggedArrays()

    {

        // Create a jagged array. Arrays of value types contain 0 at each valid

        // index. Arrays of reference types contain null at each valid index

        int[][] aJagged = new int[3][];     // Examine in Watch window

 

        // Initialize a jagged array. Note that each element of a Jagged

        // is an array of different length

        aJagged[0] = new int [] {1,2,3};

        aJagged[1] = new int[] {4,5,7,8};

        aJagged[2] = new int[] {9, 10, 11, 12, 13};

 

        // Traversing a jagged array require a foreach statement for each dimension

        // a Jagged is a 2D jagged array

        foreach (int[] aN in aJagged)

            foreach ( int n in aN)

                Trace.WriteLine(n.ToString()); 

    }

 

    /// <summary>

    /// The length of each dimension in a multidimensional array is always constant.

    /// The compiler utilizes this property to generate more efficient initialization

    /// code than that for jagged arrays

    /// </summary>

    public void TestMultiDimensionalArrays()

    {

        // Create a 2x4 2D array (2 rows and 4 columns). Arrays of value types contain

        // 0 at each valid index. Arrays of reference types contain null at each valid

        // index

        int[,] aMulti = new int[2, 4] { {1,2,3,4}, {5,6,7,8}};      // Examine in Watch window

 

        // Traverse a multi-dimensional array. Requires a single foreach statement

        // unlike jagged array. Note that it traverses all columns in first row,

        // then all columns in second row

        foreach (int n in aMulti)

            Trace.WriteLine(n.ToString()); 

    } 

}

42. Utilize Attributes to Simplify Reflection

Suppose you need to build a mechanism to dynamically add menu items and command handlers to a running software system. The requirements are for these dynamic menus simple: Drop an assembly into a directory, and the program will find out about it and add new menu items for the new command. This is one of those jobs that is best handled with reflection as existing assemblies needs to interact with assemblies that have not yet been written.

When you build systems that rely on reflection, you should define custom attributes for the types, methods, and properties you intend to use to make them easier to access. The custom attributes indicate how you intended the method to be used at runtime. Attributes can test some of the properties of the target. Testing these properties minimizes the likelihood of mistyping that can happen with reflection.

Let's begin with the code you need to create the dynamic menus framework:

Attributes make many of these tasks easier. By tagging different classes and event handlers with custom attributes, you greatly simplify your task of finding and installing those potential command handlers. In our dynamic menus framework, an attribute class marks the types that have command handlers:

// Define the Command Handler Custom Attribute. Always mark an attribute class with
// the [AttributeUsage] attribute; it tells other programmers and the compiler where
// your attribute can be use

[AttributeUsage( AttributeTargets.Class )]
public class CommandHandlerAttribute : Attribute
{
    public CommandHandlerAttribute( )
    {
    }
}

You call GetCustomAttributes to determine whether a type has the [CommandHandler] attribute. Only those types are candidates for add-ins:

// Get all the assemblies from some designated add-in directory
string[] assemblies = GetDyanmicMenuAssemblies();
foreach ( string assemblyFile in assemblies )
{
    Assembly asm = Assembly.LoadFrom( assemblyFile );

    // Find and install command handlers from the assembly.
    foreach( System.Type t in asm.GetExportedTypes( ))
    {
        if (t.GetCustomAttributes(typeof( CommandHandlerAttribute ), false ).Length > 0 )
        {
            // Found the command handler attribute on this type. This type implements a command handler.
            // configure and add it.
        }

        // Else, not a command handler. Skip it.
    }
}

Now we define a new attribute that add-in authors will attach to each command handler. To tag a command handler, you define an attribute that marks a property as a command handler and declares the text for the menu item and the text for the parent menu item. The [DynamicCommand] attribute shown below is constructed with two parameters: the command text and the text of the parent menu. This attribute class is tagged so that it can be applied only to properties. The command handler must be exposed as a property in the class that provides access to the command handler.

[AttributeUsage( AttributeTargets.Property ) ]
public class DynamicMenuAttribute : System.Attribute
{
    private string MenuText {get; set; }
    private string ParentText {get; set; }

    public DynamicMenuAttribute( string CommandText, string ParentText )
    {
        MenuText = CommandText;
        ParentText = ParentText;
    }
}

Now you'll build a sample command handler. First, you tag the type with the [CommandHandler] attribute. Inside the CmdHandler class, you add a property to retrieve the command handler. That property should be tagged with the [DynamicMenu] attribute:

[ CommandHandler ]
public class CmdHandler
{
    [DynamicMenu( "Test Command", "Parent Menu" )]
    public EventHandler CmdFunc
    {
        get
        {
            if ( theCmdHandler == null )
                theCmdHandler = new System.EventHandler(this.DynamicCommandHandler);
            return theCmdHandler;
        }
    }

    private void DynamicCommandHandler( object sender, EventArgs args )
    {
        ...
    }
}


That's it. This example shows you how you can utilize attributes to simplify programming idioms that use reflection. You tagged each type that provided a dynamic command handler with an attribute. That made it easier to find the command handlers when you dynamically loaded the assembly. By applying AttributeTargets (another attribute), you limit where the dynamic command attribute can be applied. This simplifies the difficult task of finding the sought types in a dynamically loaded assembly: You greatly decrease the chance of using the wrong types. It's still not simple code, but it is a little more palatable than without attributes.

43. Do not Overuse Reflection

When you use reflection, you circumvent C#'s type safety. Instead, the Invoke members use parameters and return values typed as System.Object. You must make sure the proper types are used at runtime. In short, using reflection makes it much easier to build dynamic programs, but it is also much easier to build broken programs. Often, with a little thought, you can minimize or remove the need for reflection by creating a set of interface definitions that express your assumptions about a type. There is nothing magical about reflection. It is a means of dynamically interacting with other binary components. In most cases, you don't need the flexibility of reflection because other alternatives are more maintainable.

For example, consider the case of creating instances of a given type. You can often accomplish the same result using a class factory. Consider this code fragment, which creates an instance of MyType by calling the default constructor using reflection:

// Usage:Create a new object using reflection:
Type t = typeof( MyType );
MyType obj = NewInstance( t ) as MyType;


// Example factory function, based on Reflection:
object NewInstance( Type t )
{
    // Find the default constructor:
    ConstructorInfo ci = t.GetConstructor( new Type[ 0 ] );

    if ( ci != null )
        // Invoke default constructor, and return the new object.
        return ci.Invoke( null );

    // If it failed, return null.
    return null;
}


The code examines the type using reflection and invokes the default constructor to create the object. If you need to create a type at runtime without any previous knowledge of the type, this is the only option. This is brittle code that relies on the presence of a default constructor. It still compiles if you remove the default constructor from MyType. You must perform runtime testing to catch any problems that arise.

Reflection is a powerful late-binding mechanism. The .NET Framework uses it to implement data binding for both Windows- and web-based controls. However, in many less general uses, creating code using class factories, delegates, and interfaces will produce more maintainable systems.

44. Create Complete Application-Specific Exception Classes

You need to be very thoughtful about when you create your own specific exception classes in your C# applications. When developers using your libraries write catch clauses, they differentiate actions based on the specific runtime type of the exception. Each different exception class can have a different set of actions taken:

try
{
    Foo( );
    Bar( );
}
catch( MyFirstApplicationException e1 )
{
    FixProblem( e1 );
}
catch( AnotherApplicationException e2 )
{
    ReportErrorAndContinue( e2 );
}
catch( YetAnotherApplicationException e3 )
{
    ReportErrorAndShutdown( e3 );
}
catch( Exception e )
{
    ReportGenericError( e );
}
catch
{
    CleanupResources( );
}

Different catch clauses can exist for different runtime types of exceptions. You, as a library author, must create or use different exception classes when catch clauses might take different actions. If you don't, your users are left with only unappealing options, such as the following:

try
{
    Foo( );
    Bar( );
}
catch( Exception e )
{
    switch( e.TargetSite.Name )
    {
        case "Foo":
            FixProblem( e );
        break;
        case "Bar":
            ReportErrorAndContinue( e );
        break;
    default:
        ReportErrorAndShutdown( e );
        break;
    }
    }
finally
{
    CleanupResources( );
}

That's far less appealing than using multiple catch clauses. It's very brittle code: If you change the name of a routine, it's broken

The reason for different exception classes is to make it easier to take different actions when your users write catch handlers. Look for those error conditions that might be candidates for some kind of recovery action, and create specific exception classes to handle those actions

You do have very specific responsibilities when you create a new exception class. You should always derive your exception classes from the System.ApplicationException class, not the System.Exception class. When you create a new exception class, create all constructors defined in System.ApplicationException class. Different situations call for the different methods of constructing exceptions. You delegate the work to the base class implementation:

public class MyAssemblyException : ApplicationException
{
    public MyAssemblyException( ) : base( )
    {
    }

    public MyAssemblyException( string s ) : base( s )
    {}

    public MyAssemblyException( string s, Exception e) : base( s, e )
    {
            }

    protected MyAssemblyException( SerializationInfo info, StreamingContext cxt ) : base( info, cxt )
    {
    }
    ...
}

You should provide your own library's information when you generate the exception. Throw your own specific exception, and include the original exception as its InnerException property. You can provide as much extra information as you can generate:

public double DoSomeWork( )
{
    try
    {
        // This might throw an exception defined in the third party library:
        return ThirdPartyLibrary.ImportantRoutine( );
    }
    catch( Exception e )
    {
        string msg = string.Format("Problem with {0} using library", this.ToString( ));
        throw new DoingSomeWorkException( msg, e );
    }
}



This new version creates more information at the point where the problem is generated. As long as you have created a proper ToString() method (see Item 5  Always Override ToString()), you've created an exception that describes the complete state of the object that generated the problem. More than that, the inner exception shows the root cause of the problem: something in the third-party library you used.

Your application will throw exceptions—hopefully not often, but it will happen. If you don't do anything specific, your application will generate the default .NET Framework exceptions whenever something goes wrong in the methods you call on the core framework. Providing more detailed information will go a long way to enabling you and your users to diagnose and possibly correct errors in the field. You create different exception classes when different corrective actions are possible and only when different actions are possible. You create full-featured exception classes by providing all the constructors that the base exception class supports. You use the InnerException property to carry along all the error information generated by lower-level error conditions.