Chapter 13. Commonly Used Interfaces and Patterns

The recipes in this chapter show you how to implement patterns you will use frequently during the development of Microsoft .NET Framework applications. Some of these patterns are formalized using interfaces defined in the .NET Framework class library. Others are less rigid, but still require you to take specific approaches to their design and implementation of your types. The recipes in this chapter describe how to do the following:

  • Create custom serializable types that you can easily store to disk, send across the network, or pass by value across application domain boundaries (recipe 13-1)

  • Provide a mechanism that creates accurate and complete copies (clones) of objects (recipe 13-2)

  • Implement types that are easy to compare and sort (recipe 13-3)

  • Support the enumeration of the elements contained in custom collections using a default or custom iterator (recipes 13-4 and 13-5)

  • Ensure that a type that uses unmanaged resources correctly releases those resources when they are no longer needed (recipe 13-6)

  • Display string representations of objects that vary based on format specifiers (recipe 13-7)

  • Correctly implement custom exception and event argument types, which you will use frequently in the development of your applications (recipes 13-8 and 13-9)

  • Implement the commonly used Singleton and Observer design patterns using the built-in features of C# and the .NET Framework class library (recipes 13-10 and 13-11)

  • Implement the producer/consumer pattern to coordinate multiple threads or tasks safely (recipe 13-12)

  • Defer initialization of a type until the first time it is used (recipe 13-13)

  • Define a method that has optional parameters with default values (recipe 13-14).

  • Define an extension method (recipe 13-15)

  • Invoke type members dynamically and without static checking (recipe 13-16)

  • Create variant generic types (recipe 13-18)

Implement a Custom Serializable Type

Problem

You need to implement a custom type that is serializable, allowing you to do the following:

  • Store instances of the type to persistent storage (for example, a file or a database)

  • Transmit instances of the type across a network

  • Pass instances of the type by value across application domain boundaries

Solution

For serialization of simple types, apply the attribute System.SerializableAttribute to the type declaration. For types that are more complex, or to control the content and structure of the serialized data, implement the interface System.Runtime.Serialization.ISerializable.

How It Works

Recipe 2-13 showed how to serialize and deserialize an object using the formatter classes provided with the .NET Framework class library. However, types are not serializable by default. To implement a custom type that is serializable, you must apply the attribute SerializableAttribute to your type declaration. As long as all of the data fields in your type are serializable types, applying SerializableAttribute is all you need to do to make your custom type serializable. If you are implementing a custom class that derives from a base class, the base class must also be serializable.

Warning

Classes that derive from a serializable type don't inherit the attribute SerializableAttribute. To make derived types serializable, you must explicitly declare them as serializable by applying the SerializableAttribute attribute.

Each formatter class contains the logic necessary to serialize types decorated with SerializableAttribute, and will correctly serialize all public, protected, and private fields. You can exclude specific fields from serialization by applying the attribute System.NonSerializedAttribute to those fields. As a rule, you should exclude the following fields from serialization:

  • Fields that contain unserializable data types

  • Fields that contain values that might be invalid when the object is deserialized, such as database connections, memory addresses, thread IDs, and unmanaged resource handles

  • Fields that contain sensitive or secret information, such as passwords, encryption keys, and the personal details of people and organizations

  • Fields that contain data that is easily re-creatable or retrievable from other sources, especially if there is a lot of data

Note

See Recipe 2-14 for an example of using a slightly different method to exclude members from serialization when using the JSON format.

If you exclude fields from serialization, you must implement your type to compensate for the fact that some data will not be present when an object is deserialized. Unfortunately, you cannot create or retrieve the missing data fields in an instance constructor, because formatters do not call constructors during the process of deserializing objects. The best approach for achieving fine-grained control of the serialization of your custom types is to use the attributes from the System.Runtime.Serialization namespace described in Table 13-1. These attributes allow you to identify methods of the serializable type that the serialization process should execute before and after serialization and deserialization. Any method annotated with one of these attributes must take a single System.Runtime.Serialization. StreamingContext argument, which contains details about the source or intended destination of the serialized object so that you can determine what to serialize. For example, you might be happy to serialize secret data if it's destined for another application domain in the same process, but not if the data will be written to a file.

Table 13.1. Attributes to Customize the Serialization and Deserialization Processs

Attribute

Description

OnSerializingAttribute

Apply this attribute to a method to have it executed before the object is serialized. This is useful if you need to modify object state before it is serialized. For example, you may need to convert a DateTime field to UTC time for storage.

OnSerializedAttribute

Apply this attribute to a method to have it executed after the object is serialized. This is useful in case you need to revert the object state to what it was before the method annotated with OnSerializingAttribute was run.

OnDeserializingAttribute

Apply this attribute to a method to have it executed before the object is deserialized. This is useful if you need to modify the object state prior to deserialization.

OnDeserializedAttribute

Apply this attribute to a method to have it executed after the object is deserialized. This is useful if you need to re-create additional object state that depends on the data that was deserialized with the object or modify the deserialized state before the object is used.

As types evolve, you often add new member variables to support new features. This new state causes a problem when deserializing old objects because the new member variables are not part of the serialized object. The.NET Framework supports the attribute System.Runtime.Serialization. OptionalFieldAttribute. When you create a new version of a type and add data members, annotate them with OptionalFieldAttribute, and the deserialization process will not fail if they are not present. You can then use a method annotated with OnDeserializedAttribute (see Table 13-1) to configure the new member variables appropriately.

For the majority of custom types, the mechanisms described will be sufficient to meet your serialization needs. If you require more control over the serialization process, you can implement the interface ISerializable. The formatter classes use different logic when serializing and deserializing instances of types that implement ISerializable. To implement ISerializable correctly, you must do the following:

  • Declare that your type implements ISerializable

  • Apply the attribute SerializableAttribute to your type declaration as just described. Do not use NonSerializedAttribute, because it will have no effect.

  • Implement the ISerializable.GetObjectData method (used during serialization), which takes the argument types System.Runtime.Serialization. SerializationInfo and System.Runtime.Serialization.StreamingContext.

  • Implement a nonpublic constructor (used during deserialization) that accepts the same arguments as the GetObjectData method. Remember that if you plan to derive classes from your serializable class, you should make the constructor protected.

During serialization, the formatter calls the GetObjectData method and passes it SerializationInfo and StreamingContext references as arguments. Your type must populate the SerializationInfo object with the data you want to serialize.

If you are creating a serializable class from a base class that also implements ISerializable, your type's GetObjectData method and deserialization constructor must call the equivalent method and constructor in the parent class.

The SerializationInfo class acts as a list of field/value pairs and provides the AddValue method to let you store a field with its value. In each call to AddValue, you must specify a name for the field/value pair; you use this name during deserialization to retrieve the value of each field. The AddValue method has 16 overloads that allow you to add values of different data types to the SerializationInfo object.

The StreamingContext object, as described earlier, provides information about the purpose and destination of the serialized data, allowing you to choose which data to serialize.

When a formatter deserializes an instance of your type, it calls the deserialization constructor, again passing a SerializationInfo and a StreamingContext reference as arguments. Your type must extract the serialized data from the SerializationInfo object using one of the SerializationInfo.Get* methods; for example, using GetString, GetInt32, or GetBoolean. During deserialization, the StreamingContext object provides information about the source of the serialized data, allowing you to mirror the logic you implemented for serialization.

The Code

This following example demonstrates a serializable Employee class that implements the ISerializable interface. In this example, the Employee class does not serialize the address field if the provided StreamingContext object specifies that the destination of the serialized data is a file. The Main method demonstrates the serialization and deserialization of an Employee object.

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    [Serializable]
    public class Employee : ISerializable
    {
        private string name;
        private int age;
        private string address;

        // Simple Employee constructor.
        public Employee(string name, int age, string address)
        {
            this.name = name;
            this.age = age;
            this.address = address;
        }

        // Constructor required to enable a formatter to deserialize an
        // Employee object. You should declare the constructor private or at
        // least protected to ensure it is not called unnecessarily.
        private Employee(SerializationInfo info, StreamingContext context)
        {
            // Extract the name and age of the employee, which will always be
            // present in the serialized data regardless of the value of the
            // StreamingContext.
            name = info.GetString("Name");
            age = info.GetInt32("Age");

            // Attempt to extract the employee's address and fail gracefully
            // if it is not available.
            try
            {
                address = info.GetString("Address");
            }
catch (SerializationException)
            {
                address = null;
            }
        }

        // Public property to provide access to employee's name.
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        // Public property to provide access to employee's age.
        public int Age
        {
            get { return age; }
            set { age = value; }
        }

        // Public property to provide access to employee's address.
        // Uses lazy initialization to establish address because
        // a deserialized object will not have an address value.
        public string Address
        {
            get
            {
                if (address == null)
                {
                    // Load the address from persistent storage.
                    // In this case, set it to an empty string.
                    address = String.Empty;
                }
                return address;
            }

            set
            {
                address = value;
            }
        }

        // Declared by the ISerializable interface, the GetObjectData method
        // provides the mechanism with which a formatter obtains the object
        // data that it should serialize.
        public void GetObjectData(SerializationInfo inf, StreamingContext con)
        {
            // Always serialize the employee's name and age.
            inf.AddValue("Name", name);
            inf.AddValue("Age", age);
// Don't serialize the employee's address if the StreamingContext
            // indicates that the serialized data is to be written to a file.
            if ((con.State & StreamingContextStates.File) == 0)
            {
                inf.AddValue("Address", address);
            }
        }

        // Override Object.ToString to return a string representation of the
        // Employee state.
        public override string ToString()
        {
            StringBuilder str = new StringBuilder();

            str.AppendFormat("Name: {0}
", Name);
            str.AppendFormat("Age: {0}
", Age);
            str.AppendFormat("Address: {0}
", Address);

            return str.ToString();
        }
    }

    // A class to demonstrate the use of Employee.
    public class Recipe13_01
    {
        public static void Main(string[] args)
        {
            // Create an Employee object representing Roger.
            Employee roger = new Employee("Roger", 56, "London");

            // Display Roger.
            Console.WriteLine(roger);

            // Serialize Roger specifying another application domain as the
            // destination of the serialized data. All data including Roger's
            // address is serialized.
            Stream str = File.Create("roger.bin");
            BinaryFormatter bf = new BinaryFormatter();
            bf.Context =
                new StreamingContext(StreamingContextStates.CrossAppDomain);
            bf.Serialize(str, roger);
            str.Close();

            // Deserialize and display Roger.
            str = File.OpenRead("roger.bin");
            bf = new BinaryFormatter();
            roger = (Employee)bf.Deserialize(str);
            str.Close();
            Console.WriteLine(roger);
// Serialize Roger specifying a file as the destination of the
            // serialized data. In this case, Roger's address is not included
            // in the serialized data.
            str = File.Create("roger.bin");
            bf = new BinaryFormatter();
            bf.Context = new StreamingContext(StreamingContextStates.File);
            bf.Serialize(str, roger);
            str.Close();

            // Deserialize and display Roger.
            str = File.OpenRead("roger.bin");
            bf = new BinaryFormatter();
            roger = (Employee)bf.Deserialize(str);
            str.Close();
            Console.WriteLine(roger);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement a Cloneable Type

Problem

You need to create a custom type that provides a simple mechanism for programmers to create copies of type instances.

Solution

Implement the System.ICloneable interface.

How It Works

When you assign one value type to another, you create a copy of the value. No link exists between the two values—a change to one will not affect the other. However, when you assign one reference type to another (excluding strings, which receive special treatment by the runtime), you do not create a new copy of the reference type. Instead, both reference types refer to the same object, and changes to the value of the object are reflected in both references. To create a true copy of a reference type, you must clone the object to which it refers.

The ICloneable interface identifies a type as cloneable and declares the Clone method as the mechanism through which you obtain a clone of an object. The Clone method takes no arguments, and returns a System.Object, regardless of the implementing type. This means that once you clone an object, you must explicitly cast the clone to the correct type.

The approach you take to implement the Clone method for a custom type depends on the data members declared within the type. If the custom type contains only value-type data members (int, byte, and so on) and System.String data members, you can implement the Clone method by instantiating a new object and setting its data members to the same values as the current object. The Object class (from which all types derive) includes the protected method MemberwiseClone, which automates this process.

If your custom type contains reference-type data members, you must decide whether your Clone method will perform a shallow copy or a deep copy. A shallow copy means that any reference-type data members in the clone will refer to the same objects as the equivalent reference-type data members in the original object. A deep copy means that you must create clones of the entire object graph so that the reference-type data members of the clone refer to physically independent copies (clones) of the objects referenced by the original object.

A shallow copy is easy to implement using the MemberwiseClone method just described. However, a deep copy is often what programmers expect when they first clone an object, but it's rarely what they get. This is especially true of the collection classes in the System.Collections namespace, which all implement shallow copies in their Clone methods. Although it would often be useful if these collections implemented a deep copy, there are two key reasons why types (especially generic collection classes) do not implement deep copies:

  • Creating a clone of a large object graph is processor-intensive and memory-intensive.

  • General-purpose collections can contain wide and deep object graphs consisting of any type of object. Creating a deep-copy implementation to cater to such variety is not feasible because some objects in the collection might not be cloneable, and others might contain circular references, which would send the cloning process into an infinite loop.

For strongly typed collections in which the nature of the contained elements are understood and controlled, a deep copy can be a very useful feature; for example, System.Xml.XmlNode implements a deep copy in its Clone method. This allows you to create true copies of entire XML object hierarchies with a single statement.

Tip

If you need to clone an object that does not implement ICloneable but is serializable, you can often serialize and then deserialize the object to achieve the same result as cloning. However, be aware that the serialization process might not serialize all data members (as discussed in recipe 13-1). Likewise, if you create a custom serializable type, you can potentially use the serialization process just described to perform a deep copy within your ICloneable.Clone method implementation. To clone a serializable object, use the class System.Runtime.Serialization.Formatters.Binary.BinaryFormatter to serialize the object to, and then deserialize the object from a System.IO.MemoryStream object.

The Code

The following example demonstrates various approaches to cloning. The simple class Employee contains only string and int members, and so relies on the inherited MemberwiseClone method to create a clone. The Team class contains an implementation of the Clone method that performs a deep copy. The Team class contains a collection of Employee objects, representing a team of people. When you call the Clone method of a Team object, the method creates a clone of every contained Employee object and adds it to the cloned Team object. The Team class provides a private constructor to simplify the code in the Clone method. The use of constructors is a common approach to simplifying the cloning process.

using System;
using System.Text;
using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    public class Employee : ICloneable
    {
        public string Name;
        public string Title;
        public int Age;

        // Simple Employee constructor.
        public Employee(string name, string title, int age)
        {
            Name = name;
            Title = title;
            Age = age;
        }

        // Create a clone using the Object.MemberwiseClone method because the
        // Employee class contains only string and value types.
        public object Clone()
        {
            return MemberwiseClone();
        }

        // Returns a string representation of the Employee object.
        public override string ToString()
        {
            return string.Format("{0} ({1}) - Age {2}", Name, Title, Age);
        }
    }

    public class Team : ICloneable
    {
        // A List to hold the Employee team members.
        public List<Employee> TeamMembers =
            new List<Employee>();
public Team()
        {
        }

        // Private constructor called by the Clone method to create a new Team
        // object and populate its List with clones of Employee objects from
        // a provided List.
        private Team(List<Employee> members)
        {
            foreach (Employee e in members)
            {
                // Clone the individual employee objects and
                // add them to the List.
                TeamMembers.Add((Employee)e.Clone());
            }
        }

        // Adds an Employee object to the Team.
        public void AddMember(Employee member)
        {
            TeamMembers.Add(member);
        }

        // Override Object.ToString to return a string representation of the
        // entire Team.
        public override string ToString()
        {
            StringBuilder str = new StringBuilder();

            foreach (Employee e in TeamMembers)
            {
                str.AppendFormat("  {0}
", e);
            }

            return str.ToString();
        }

        // Implementation of ICloneable.Clone.
        public object Clone()
        {
            // Create a deep copy of the team by calling the private Team
            // constructor and passing the ArrayList containing team members.
            return new Team(this.TeamMembers);

            // The following command would create a shallow copy of the Team.
            // return MemberwiseClone();
        }
    }
// A class to demonstrate the use of Employee.
    public class Recipe13_02
    {
        public static void Main()
        {
            // Create the original team.
            Team team = new Team();
            team.AddMember(new Employee("Frank", "Developer", 34));
            team.AddMember(new Employee("Kathy", "Tester", 78));
            team.AddMember(new Employee("Chris", "Support", 18));

            // Clone the original team.
            Team clone = (Team)team.Clone();

            // Display the original team.
            Console.WriteLine("Original Team:");
            Console.WriteLine(team);

            // Display the cloned team.
            Console.WriteLine("Clone Team:");
            Console.WriteLine(clone);

            // Make change.
            Console.WriteLine("*** Make a change to original team ***");
            Console.WriteLine(Environment.NewLine);
            team.TeamMembers[0].Name = "Luke";
            team.TeamMembers[0].Title = "Manager";
            team.TeamMembers[0].Age = 44;

            // Display the original team.
            Console.WriteLine("Original Team:");
            Console.WriteLine(team);

            // Display the cloned team.
            Console.WriteLine("Clone Team:");
            Console.WriteLine(clone);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement a Comparable Type

Problem

You need to provide a mechanism that allows you to compare custom types, enabling you to easily sort collections containing instances of those types.

Solution

To provide a standard comparison mechanism for a type, implement the generic System.IComparable<T> interface. To support the comparison of a type based on more than one characteristic, create separate types that implement the generic System.Collections.Generic.IComparer<T> interface.

How It Works

If you need to sort your type into only a single order, such as ascending ID number, or alphabetically based on surname, you should implement the IComparable<T> interface. IComparable<T> defines a single method named CompareTo, shown here:

int CompareTo(T other);

The value returned by CompareTo should be calculated as follows:

  • If the current object is less than other, return less than zero (for example,−1).

  • If the current object has the same value as other, return zero.

  • If the current object is greater than other, return greater than zero (for example, 1).

What these comparisons mean depends on the type implementing the IComparable interface. For example, if you were sorting people based on their surname, you would do a String comparison on this field. However, if you wanted to sort by birthday, you would need to perform a comparison of the corresponding System.DateTime fields.

To support a variety of sort orders for a particular type, you must implement separate helper types that implement the IComparer<T> interface, which defines the Compare method shown here:

int Compare(T x, T y);

These helper types must encapsulate the necessary logic to compare two objects and return a value based on the following logic:

  • If x is less than y, return less than zero (for example, −1).

  • If x has the same value as y, return zero.

  • If x is greater than y, return greater than zero (for example, 1).

The Code

The Newspaper class listed here demonstrates the implementation of both the IComparable and IComparer interfaces. The Newspaper.CompareTo method performs a case-insensitive comparison of two Newspaper objects based on their name fields. A private nested class named AscendingCirculationComparer implements IComparer and compares two Newspaper objects based on their circulation fields. An AscendingCirculationComparer object is obtained using the static Newspaper.CirculationSorter property.

The Main method shown here demonstrates the comparison and sorting capabilities provided by implementing the IComparable and IComparer interfaces. The method creates a System.Collections. ArrayList collection containing five Newspaper objects. Main then sorts the ArrayList twice using the ArrayList.Sort method. The first Sort operation uses the default Newspaper comparison mechanism provided by the IComparable.CompareTo method. The second Sort operation uses an AscendingCirculationComparer object to perform comparisons through its implementation of the IComparer.Compare method.

using System;
using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    public class Newspaper : IComparable<Newspaper>
    {
        private string name;
        private int circulation;

        private class AscendingCirculationComparer : IComparer<Newspaper>
        {
            // Implementation of IComparer.Compare. The generic definition of
            // IComparer allows us to ensure both arguments are Newspaper
            // objects.
            public int Compare(Newspaper x, Newspaper y)
            {
                // Handle logic for null reference as dictated by the
                // IComparer interface. Null is considered less than
                // any other value.
                if (x == null && y == null) return 0;
                else if (x == null) return −1;
                else if (y == null) return 1;

                // Short-circuit condition where x and y are references
                // to the same object.
                if (x == y) return 0;

                // Compare the circulation figures. IComparer dictates that:
                //      return less than zero if x < y
                //      return zero if x = y
                //      return greater than zero if x > y
// This logic is easily implemented using integer arithmetic.
                return x.circulation - y.circulation;
            }
        }

        // Simple Newspaper constructor.
        public Newspaper(string name, int circulation)
        {
            this.name = name;
            this.circulation = circulation;
        }

        // Declare a read-only property that returns an instance of the
        // AscendingCirculationComparer.
        public static IComparer<Newspaper> CirculationSorter
        {
            get { return new AscendingCirculationComparer(); }
        }

        // Override Object.ToString.
        public override string ToString()
        {
            return string.Format("{0}: Circulation = {1}", name, circulation);
        }

        // Implementation of IComparable.CompareTo. The generic definition
        // of IComparable allows us to ensure that the argument provided
        // must be a Newspaper object. Comparison is based on a
        // case-insensitive comparison of the Newspaper names.
        public int CompareTo(Newspaper other)
        {
            // IComparable dictates that an object is always considered greater
            // than null.
            if (other == null) return 1;

            // Short-circuit the case where the other Newspaper object is a
            // reference to this one.
            if (other == this) return 0;

            // Calculate return value by performing a case-insensitive
            // comparison of the Newspaper names.

            // Because the Newspaper name is a string, the easiest approach
            // is to rely on the comparison capabilities of the String
            // class, which perform culture-sensitive string comparisons.
            return string.Compare(this.name, other.name, true);
        }
    }
// A class to demonstrate the use of Newspaper.
    public class Recipe13_03
    {
        public static void Main()
        {
            List<Newspaper> newspapers = new List<Newspaper>();

            newspapers.Add(new Newspaper("The Echo", 125780));
            newspapers.Add(new Newspaper("The Times", 55230));
            newspapers.Add(new Newspaper("The Gazette", 235950));
            newspapers.Add(new Newspaper("The Sun", 88760));
            newspapers.Add(new Newspaper("The Herald", 5670));

            Console.Clear();
            Console.WriteLine("Unsorted newspaper list:");
            foreach (Newspaper n in newspapers)
            {
                Console.WriteLine("  " + n);
            }

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Newspaper list sorted by name (default order):");
            newspapers.Sort();
            foreach (Newspaper n in newspapers)
            {
                Console.WriteLine("  " + n);
            }

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Newspaper list sorted by circulation:");
            newspapers.Sort(Newspaper.CirculationSorter);
            foreach (Newspaper n in newspapers)
            {
                Console.WriteLine("  " + n);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Usage

Running the example will produce the results shown here. The first list of newspapers is unsorted, the second is sorted using the IComparable interface, and the third is sorted using a comparer class that implements IComparer.

Unsorted newspaper list:

The Echo: Circulation = 125780

The Times: Circulation = 55230

The Gazette: Circulation = 235950

The Sun: Circulation = 88760

The Herald: Circulation = 5670



Newspaper list sorted by name (default order):

The Echo: Circulation = 125780

The Gazette: Circulation = 235950

The Herald: Circulation = 5670

The Sun: Circulation = 88760

The Times: Circulation = 55230



Newspaper list sorted by circulation:

The Herald: Circulation = 5670

The Times: Circulation = 55230

The Sun: Circulation = 88760

The Echo: Circulation = 125780

The Gazette: Circulation = 235950

Implement an Enumerable Collection

Problem

You need to create a collection type whose contents you can enumerate using a foreach statement.

Solution

Implement the generic interface System.Collections.Generic.IEnumerable<T> on your collection type. The GetEnumerator method of the IEnumerable interface returns an enumerator, which is an object that implements the interface System.Collections.Generic.IEnumerator<T>. Within the GetEnumerator method, traverse the items in the collection using whatever logic is appropriate to your data structure and return the next value using the yield return statement. The C# compiler will automatically generate the necessary code to enable enumeration across the contents of your type.

How It Works

A numeric indexer allows you to iterate through the elements of most standard collections using a for loop. However, this technique does not always provide an appropriate abstraction for nonlinear data structures, such as trees and multidimensional collections. The foreach statement provides an easy-to-use and syntactically elegant mechanism for iterating through a collection of objects, regardless of their internal structures.

In order to support foreach semantics, the type containing the collection of objects should implement the IEnumerable<T> interface. The IEnumerable<T> interface declares a single method named GetEnumerator, which takes no arguments and returns an object that implements IEnumerator<T>. All you need to do in your GetEnumerator method is write the code necessary to iterate through the items in your collection using logic appropriate to the data structure. Each time you want to return an item, call the yield return statement and specify the value to return. The compiler generates code that returns the specified value and maintains appropriate state for the next time a value is requested. If you need to stop partway through the enumeration, call the yield break statement instead, and the enumeration will terminate as if it had reached the end of the collection.

Tip

You do not actually need to explicitly implement IEnumerable on your type to make it enumerable. As long as it has a GetEnumerator method that returns an IEnumerator instance, the compiler will allow you to use the type in a foreach statement. However, it is always good practice to explicitly declare the capabilities of a type by declaring the interfaces it implements, as it allows users of your class to more easily understand its capabilities and purpose.

The GetEnumerator method is used automatically whenever you use an instance of your collection type in a foreach statement. However, if you want to provide multiple ways to enumerate the items in your collection, you can implement multiple methods or properties that are declared to return IEnumerable<T> instances. Within the body of the member, use the yield return statement just mentioned, and the C# compiler will generate the appropriate code automatically. To use one of the alternative enumerations from a foreach statement, you must directly reference the appropriate member, as in this example:

foreach (node n in Tree.BreadthFirst)

The Code

The following example demonstrates the creation of an enumerable collection using the IEnumerable<T> and IEnumerator<T> interfaces in conjunction with the yield return and yield break statements. The Team class, which represents a team of people, is a collection of enumerable TeamMember objects.

using System;
using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // The TeamMember class represents an individual team member.
    public class TeamMember
    {
        public string Name;
        public string Title;

        // Simple TeamMember constructor.
        public TeamMember(string name, string title)
        {
            Name = name;
            Title = title;
        }

        // Returns a string representation of the TeamMember.
        public override string ToString()
        {
            return string.Format("{0} ({1})", Name, Title);
        }
    }

    // Team class represents a collection of TeamMember objects.
    public class Team
    {
        // A List to contain the TeamMember objects.
        private List<TeamMember> teamMembers = new List<TeamMember>();
// Implement the GetEnumerator method, which will support
        // iteration across the entire team member List.
        public IEnumerator<TeamMember> GetEnumerator()
        {
            foreach (TeamMember tm in teamMembers)
            {
                yield return tm;
            }
        }

        // Implement the Reverse method, which will iterate through
        // the team members in alphabetical order.
        public IEnumerable<TeamMember> Reverse
        {
            get
            {
                for (int c = teamMembers.Count - 1; c >= 0; c--)
                {
                    yield return teamMembers[c];
                }
            }
        }

        // Implement the FirstTwo method, which will stop the iteration
        // after only the first two team members.
        public IEnumerable<TeamMember> FirstTwo
        {
            get
            {
                int count = 0;

                foreach (TeamMember tm in teamMembers)
                {
                    if (count >= 2)
                    {
                        // Terminate the iterator.
                        yield break;
                    }
                    else
                    {
                        // Return the TeamMember and maintain the iterator.
                        count++;
                        yield return tm;
                    }
                }
            }
        }
// Adds a TeamMember object to the Team.
        public void AddMember(TeamMember member)
        {
            teamMembers.Add(member);
        }
    }

    // A class to demonstrate the use of Team.
    public class Recipe13_04
    {
        public static void Main()
        {
            // Create and populate a new Team.
            Team team = new Team();
            team.AddMember(new TeamMember("Curly", "Clown"));
            team.AddMember(new TeamMember("Nick", "Knife Thrower"));
            team.AddMember(new TeamMember("Nancy", "Strong Man"));

            // Enumerate the entire Team using the default iterator.
            Console.Clear();
            Console.WriteLine("Enumerate using default iterator:");
            foreach (TeamMember member in team)
            {
                Console.WriteLine("  " + member.ToString());
            }

            // Enumerate the first two Team members only.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Enumerate using the FirstTwo iterator:");
            foreach (TeamMember member in team.FirstTwo)
            {
                Console.WriteLine("  " + member.ToString());
            }

            // Enumerate the entire Team in reverse order.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Enumerate using the Reverse iterator:");
            foreach (TeamMember member in team.Reverse)
            {
                Console.WriteLine("  " + member.ToString());
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement an Enumerable Type Using a Custom Iterator

Problem

You need to create an enumerable type but do not want to rely on the built-in iterator support provided by the .NET Framework (described in recipe 13-4).

Solution

Implement the interface System.Collections.IEnumerable on your collection type. The GetEnumerator method of the IEnumerable interface returns an enumerator, which is an object that implements the interface System.Collections.IEnumerator. The IEnumerator interface defines the methods used by the foreach statement to enumerate the collection.

Implement a private inner class within the enumerable type that implements the interface IEnumerator and can iterate over the enumerable type while maintaining appropriate state information. In the GetEnumerator method of the enumerable type, create and return an instance of the iterator class.

How It Works

The automatic iterator support built into C# is very powerful and will be sufficient in the majority of cases. However, in some cases you may want to take direct control of the implementation of your collection's iterators. For example, you may want an iterator that supports changes to the underlying collection during enumeration.

Whatever your reason, the basic model of an enumerable collection is the same as that described in recipe 13-4. Your enumerable type should implement the IEnumerable interface, which requires you to implement a method named GetEnumerator. However, instead of using the yield return statement in GetEnumerator, you must instantiate and return an object that implements the IEnumerator interface. The IEnumerator interface provides a read-only, forward-only cursor for accessing the members of the underlying collection. Table 13-2 describes the members of the IEnumerator interface. The IEnumerator instance returned by GetEnumerator is your custom iterator—the object that actually supports enumeration of the collection's data elements.

Table 13.2. Members of the IEnumerator Interface

Member

Description

Current

Property that returns the current data element. When the enumerator is created, Current refers to a position preceding the first data element. This means you must call MoveNext before using Current. If Current is called and the enumerator is positioned before the first element or after the last element in the data collection, Current must throw a System.InvalidOperationException.

MoveNext

Method that moves the enumerator to the next data element in the collection. Returns true if there are more elements; otherwise, it returns false. If the underlying source of data changes during the life of the enumerator, MoveNext must throw an InvalidOperationException.

Reset

Method that moves the enumerator to a position preceding the first element in the data collection. If the underlying source of data changes during the life of the enumerator, Reset must throw an InvalidOperationException.

If your collection class contains different types of data that you want to enumerate separately, implementing the IEnumerable interface on the collection class is insufficient. In this case, you would implement a number of properties that return different IEnumerator instances.

The Code

The TeamMember, Team, and TeamMemberEnumerator classes in the following example demonstrate the implementation of a custom iterator using the IEnumerable and IEnumerator interfaces. The TeamMember class represents a member of a team. The Team class, which represents a team of people, is a collection of TeamMember objects. Team implements the IEnumerable interface and declares a separate class, named TeamMemberEnumerator, to provide enumeration functionality. Team implements the Observer pattern using delegate and event members to notify all TeamMemberEnumerator objects if their underlying Team changes. (See recipe 13-11 for a detailed description of the Observer pattern.) The TeamMemberEnumerator class is a private nested class, so you cannot create instances of it other than through the Team.GetEnumerator method.

using System;
using System.Collections;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // TeamMember class represents an individual team member.
    public class TeamMember
    {
        public string Name;
        public string Title;
// Simple TeamMember constructor.
        public TeamMember(string name, string title)
        {
            Name = name;
            Title = title;
        }

        // Returns a string representation of the TeamMember.
        public override string ToString()
        {
            return string.Format("{0} ({1})", Name, Title);
        }
    }

    // Team class represents a collection of TeamMember objects. Implements
    // the IEnumerable interface to support enumerating TeamMember objects.
    public class Team : IEnumerable
    {
        // TeamMemberEnumerator is a private nested class that provides
        // the functionality to enumerate the TeamMembers contained in
        // a Team collection. As a nested class, TeamMemberEnumerator
        // has access to the private members of the Team class.
        private class TeamMemberEnumerator : IEnumerator
        {
            // The Team that this object is enumerating.
            private Team sourceTeam;

            // Boolean to indicate whether underlying Team has changed
            // and so is invalid for further enumeration.
            private bool teamInvalid = false;

            // Integer to identify the current TeamMember. Provides
            // the index of the TeamMember in the underlying ArrayList
            // used by the Team collection. Initialize to −1, which is
            // the index prior to the first element.
            private int currentMember = −1;

            // Constructor takes a reference to the Team that is the source
            // of enumerated data.
            internal TeamMemberEnumerator(Team team)
            {
                this.sourceTeam = team;

                // Register with sourceTeam for change notifications.
                sourceTeam.TeamChange +=
                    new TeamChangedEventHandler(this.TeamChange);
            }
// Implement the IEnumerator.Current property.
            public object Current
            {
                get
                {
                    // If the TeamMemberEnumerator is positioned before
                    // the first element or after the last element, then
                    // throw an exception.
                    if (currentMember == −1 ||
                        currentMember > (sourceTeam.teamMembers.Count - 1))
                    {
                        throw new InvalidOperationException();
                    }

                    //Otherwise, return the current TeamMember.
                    return sourceTeam.teamMembers[currentMember];
                }
            }

            // Implement the IEnumerator.MoveNext method.
            public bool MoveNext()
            {
                // If underlying Team is invalid, throw exception.
                if (teamInvalid)
                {
                    throw new InvalidOperationException("Team modified");
                }

                // Otherwise, progress to the next TeamMember.
                currentMember++;

                // Return false if we have moved past the last TeamMember.
                if (currentMember > (sourceTeam.teamMembers.Count - 1))
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }

            // Implement the IEnumerator.Reset method.
            // This method resets the position of the TeamMemberEnumerator
            // to the beginning of the TeamMembers collection.
            public void Reset()
            {
// If underlying Team is invalid, throw exception.
                if (teamInvalid)
                {
                    throw new InvalidOperationException("Team modified");
                }

                // Move the currentMember pointer back to the index
                // preceding the first element.
                currentMember = −1;
            }

            // An event handler to handle notifications that the underlying
            // Team collection has changed.
            internal void TeamChange(Team t, EventArgs e)
            {
                // Signal that the underlying Team is now invalid.
                teamInvalid = true;
            }
        }

        // A delegate that specifies the signature that all team change event
        // handler methods must implement.
        public delegate void TeamChangedEventHandler(Team t, EventArgs e);

        // An ArrayList to contain the TeamMember objects.
        private ArrayList teamMembers;

        // The event used to notify TeamMemberEnumerators that the Team
        // has changed.
        public event TeamChangedEventHandler TeamChange;

        // Team constructor.
        public Team()
        {
            teamMembers = new ArrayList();
        }

        // Implement the IEnumerable.GetEnumerator method.
        public IEnumerator GetEnumerator()
        {
            return new TeamMemberEnumerator(this);
        }

        // Adds a TeamMember object to the Team.
        public void AddMember(TeamMember member)
        {
            teamMembers.Add(member);
// Notify listeners that the list has changed.
            if (TeamChange != null)
            {
                TeamChange(this, null);
            }
        }
    }

    // A class to demonstrate the use of Team.
    public class Recipe13_05
    {
        public static void Main()
        {
            // Create a new Team.
            Team team = new Team();
            team.AddMember(new TeamMember("Curly", "Clown"));
            team.AddMember(new TeamMember("Nick", "Knife Thrower"));
            team.AddMember(new TeamMember("Nancy", "Strong Man"));

            // Enumerate the Team.
            Console.Clear();
            Console.WriteLine("Enumerate with foreach loop:");
            foreach (TeamMember member in team)
            {
                Console.WriteLine(member.ToString());
            }

            // Enumerate using a While loop.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Enumerate with while loop:");
            IEnumerator e = team.GetEnumerator();
            while (e.MoveNext())
            {
                Console.WriteLine(e.Current);
            }

            // Enumerate the Team and try to add a Team Member.
            // (This will cause an exception to be thrown.)
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Modify while enumerating:");
            foreach (TeamMember member in team)
            {
                Console.WriteLine(member.ToString());
                team.AddMember(new TeamMember("Stumpy", "Lion Tamer"));
            }
// Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

The example enumerates through the data with foreach and while loops and then attempts to modify the data during an enumeration, resulting in an exception. The output from the example is as follows:

Enumerate with foreach loop:

Curly (Clown)

Nick (Knife Thrower)

Nancy (Strong Man)


Enumerate with while loop:

Curly (Clown)

Nick (Knife Thrower)

Nancy (Strong Man)


Modify while enumerating:

Curly (Clown)
Unhandled Exception: System.InvalidOperationException: Team modified

   at Apress.VisualCSharpRecipes.Chapter13.Team.TeamMemberEnumerator.MoveNext() in

C:UsersAdamDocumentsWorkC# CookbookRepositoryCSHARPRECIPESSourceCode

   Chapter13Recipe13-05Recipe13-05.cs:line 85

   at Apress.VisualCSharpRecipes.Chapter13.Recipe13_05.Main() in C:UsersAdam

    DocumentsWorkC# CookbookRepositoryCSH

ARPRECIPESSourceCodeChapter13Recipe13-05Recipe13-05.cs:line 195

Press any key to continue . . .

Implement a Disposable Class

Problem

You need to create a class that references unmanaged resources and provide a mechanism for users of the class to free those unmanaged resources deterministically.

Solution

Implement the System.IDisposable interface and release the unmanaged resources when client code calls the IDisposable.Dispose method.

How It Works

An unreferenced object continues to exist on the managed heap and consume resources until the garbage collector releases the object and reclaims the resources. The garbage collector will automatically free managed resources (such as memory), but it will not free unmanaged resources (such as file handles and database connections) referenced by managed objects. If an object contains data members that reference unmanaged resources, the object must free those resources explicitly.

One solution is to declare a destructor—or finalizer—for the class (destructor is a C++ term equivalent to the more general .NET term finalizer). Prior to reclaiming the memory consumed by an instance of the class, the garbage collector calls the object's finalizer. The finalizer can take the necessary steps to release any unmanaged resources. Unfortunately, because the garbage collector uses a single thread to execute all finalizers, use of finalizers can have a detrimental effect on the efficiency of the garbage collection process, which will affect the performance of your application. In addition, you cannot control when the runtime frees unmanaged resources because you cannot call an object's finalizer directly, and you have only limited control over the activities of the garbage collector using the System.GC class.

As a complementary mechanism to using finalizers, the .NET Framework defines the Dispose pattern as a means to provide deterministic control over when to free unmanaged resources. To implement the Dispose pattern, a class must implement the IDisposable interface, which declares a single method named Dispose. In the Dispose method, you must implement the code necessary to release any unmanaged resources and remove the object from the list of objects eligible for finalization if a finalizer has been defined.

Instances of classes that implement the Dispose pattern are called disposable objects. When code has finished with a disposable object, it calls the object's Dispose method to free all resources and make it unusable, but still relies on the garbage collector to eventually release the object memory. It's important to understand that the runtime does not enforce disposal of objects; it's the responsibility of the client to call the Dispose method. However, because the .NET Framework class library uses the Dispose pattern extensively, C# provides the using statement to simplify the correct use of disposable objects. The following code shows the structure of a using statement:

using (FileStream fileStream = new FileStream("SomeFile.txt", FileMode.Open)) {
    // Do something with the fileStream object.
}

When the code reaches the end of the block in which the disposable object was declared, the object's Dispose method is automatically called, even if an exception is raised. Here are some points to consider when implementing the Dispose pattern:

  • Client code should be able to call the Dispose method repeatedly with no adverse effects.

  • In multithreaded applications, it's important that only one thread execute the Dispose method at a time. It's normally the responsibility of the client code to ensure thread synchronization, although you could decide to implement synchronization within the Dispose method.

  • The Dispose method should not throw exceptions.

  • Because the Dispose method does all necessary cleaning up, you do not need to call the object's finalizer. Your Dispose method should call the GC.SuppressFinalize method to ensure that the finalizer is not called during garbage collection.

  • Implement a finalizer that calls the unmanaged cleanup part of your Dispose method as a safety mechanism in case client code does not call Dispose correctly. However, avoid referencing managed objects in finalizers, because you cannot be certain of the object's state.

  • If a disposable class extends another disposable class, the Dispose method of the child must call the Dispose method of its base class. Wrap the child's code in a try block and call the parent's Dispose method in a finally clause to ensure execution.

  • Other instance methods and properties of the class should throw a System.ObjectDisposedException exception if client code attempts to execute a method on an already disposed object.

The Code

The following example demonstrates a common implementation of the Dispose pattern.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // Implement the IDisposable interface.
    public class DisposeExample : IDisposable
    {
        // Private data member to signal if the object has already been
        // disposed.
        bool isDisposed = false;

        // Private data member that holds the handle to an unmanaged resource.
        private IntPtr resourceHandle;

        // Constructor.
        public DisposeExample()
        {
            // Constructor code obtains reference to unmanaged resource.
            resourceHandle = default(IntPtr);
        }

        // Destructor/finalizer. Because Dispose calls GC.SuppressFinalize,
        // this method is called by the garbage collection process only if
        // the consumer of the object does not call Dispose as it should.
        ~DisposeExample()
        {
            // Call the Dispose method as opposed to duplicating the code to
            // clean up any unmanaged resources. Use the protected Dispose
            // overload and pass a value of "false" to indicate that Dispose is
            // being called during the garbage collection process, not by
            // consumer code.
            Dispose(false);
        }

        // Public implementation of the IDisposable.Dispose method, called
        // by the consumer of the object in order to free unmanaged resources
        // deterministically.
        public void Dispose()
        {
            // Call the protected Dispose overload and pass a value of "true"
            // to indicate that Dispose is being called by consumer code, not
            // by the garbage collector.
            Dispose(true);

            // Because the Dispose method performs all necessary cleanup,
            // ensure the garbage collector does not call the class destructor.
            GC.SuppressFinalize(this);
        }
// Protected overload of the Dispose method. The disposing argument
        // signals whether the method is called by consumer code (true), or by
        // the garbage collector (false). Note that this method is not part of
        // the IDisposable interface because it has a different signature to the
        // parameterless Dispose method.
        protected virtual void Dispose(bool disposing)
        {
            // Don't try to dispose of the object twice.
            if (!isDisposed)
            {
                // Determine if consumer code or the garbage collector is
                // calling. Avoid referencing other managed objects during
                // finalization.
                if (disposing)
                {
                    // Method called by consumer code. Call the Dispose method
                    // of any managed data members that implement the
                    // IDisposable interface.
                    // ...
                }

                // Whether called by consumer code or the garbage collector,
                // free all unmanaged resources and set the value of managed
                // data members to null.
                // Close(resourceHandle);

                // In the case of an inherited type, call base.Dispose(disposing).
            }

            // Signal that this object has been disposed.
            isDisposed = true;
        }

        // Before executing any functionality, ensure that Dispose has not
        // already been executed on the object.
        public void SomeMethod()
        {
            // Throw an exception if the object has already been disposed.
            if (isDisposed)
            {
                throw new ObjectDisposedException("DisposeExample");
            }

            // Execute method functionality.
            // . . .
        }
    }

    // A class to demonstrate the use of DisposeExample.
    public class Recipe13_06
    {
public static void Main()
        {
            // The using statement ensures the Dispose method is called
            // even if an exception occurs.
            using (DisposeExample d = new DisposeExample())
            {
                // Do something with d.
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement a Formattable Type

Problem

You need to implement a type that can create different string representations of its content based on the use of format specifiers, for use in formatted strings.

Solution

Implement the System.IFormattable interface.

How It Works

The following code fragment demonstrates the use of format specifiers in the WriteLine method of the System.Console class. The format specifiers are inside the braces (shown in bold in the example).

double a = 345678.5678;
uint b = 12000;
byte c = 254;
Console.WriteLine("a = {0}, b = {1}, and c = {2}", a, b, c);
Console.WriteLine("a = {0:c0}, b = {1:n4}, and c = {2,10:x5}", a, b, c);

When run on a machine configured with English (UK) regional settings, this code will result in the output shown here:

a = 345678.5678, b = 12000, and c = 254

a = £345,679, b = 12,000.0000, and c =      000fe

As you can see, changing the contents of the format specifiers changes the format of the output significantly, even though the data has not changed. To enable support for format specifiers in your own types, you must implement the IFormattable interface. IFormattable declares a single method named ToString with the following signature:

string ToString(string format, IFormatProvider formatProvider);

The format argument is a System.String containing a format string. The format string is the portion of the format specifier that follows the colon. For example, in the format specifier {2,10:x5} used in the previous example, x5 is the format string. The format string contains the instructions that the IFormattable instance should use when it's generating the string representation of its content. The .NET Framework documentation for IFormattable states that types that implement IFormattable must support the G (general) format string, but that the other supported format strings depend on the implementation. The format argument will be null if the format specifier does not include a format string component; for example, {0} or {1,20}.

The formatProvider argument is a reference to an instance of a type that implements System.IFormatProvider, and that provides access to information about the cultural and regional preferences to use when generating the string representation of the IFormattable object. This information includes data such as the appropriate currency symbol or number of decimal places to use. By default, formatProvider is null, which means you should use the current thread's regional and cultural settings, available through the static method CurrentCulture of the System.Globalization.CultureInfo class. Some methods that generate formatted strings, such as String.Format, allow you to specify an alternative IFormatProvider to use such as CultureInfo, DateTimeFormatInfo, or NumberFormatInfo.

The .NET Framework uses IFormattable primarily to support the formatting of value types, but it can be used to good effect with any type.

The Code

The following example contains a class named Person that implements the IFormattable interface. The Person class contains the title and names of a person and will render the person's name in different formats depending on the format strings provided. The Person class does not make use of regional and cultural settings provided by the formatProvider argument. The Main method demonstrates how to use the formatting capabilities of the Person class.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    public class Person : IFormattable
    {
// Private members to hold the person's title and name details.
        private string title;
        private string[] names;

        // Constructor used to set the person's title and names.
        public Person(string title, params string[] names)
        {
            this.title = title;
            this.names = names;
        }

        // Override the Object.ToString method to return the person's
        // name using the general format.
        public override string ToString()
        {
            return ToString("G", null);
        }

        // Implementation of the IFormattable.ToString method to return the
        // person's name in different forms based on the format string
        // provided.
        public string ToString(string format, IFormatProvider formatProvider)
        {
            string result = null;

            // Use the general format if none is specified.
            if (format == null) format = "G";

            // The contents of the format string determine the format of the
            // name returned.
            switch (format.ToUpper()[0])
            {
                case 'S':
                    // Use short form - first initial and surname.
                    result = names[0][0] + ". " + names[names.Length - 1];
                    break;

                case 'P':
                    // Use polite form - title, initials, and surname.
                    // Add the person's title to the result.
                    if (title != null && title.Length != 0)
                    {
                        result = title + ". ";
                    }
                    // Add the person's initials and surname.
                    for (int count = 0; count < names.Length; count++)
                    {
                        if (count != (names.Length - 1))
                        {
                            result += names[count][0] + ". ";
                        }
else
                        {
                            result += names[count];
                        }
                    }
                    break;

                case 'I':
                    // Use informal form - first name only.
                    result = names[0];
                    break;

                case 'G':
                default:
                    // Use general/default form - first name and surname.
                    result = names[0] + " " + names[names.Length - 1];
                    break;
            }
            return result;
        }
    }

    // A class to demonstrate the use of Person.
    public class Recipe13_07
    {
        public static void Main()
        {
            // Create a Person object representing a man with the name
            // Mr. Richard Glen David Peters.
            Person person =
                new Person("Mr", "Richard", "Glen", "David", "Peters");

            // Display the person's name using a variety of format strings.
            System.Console.WriteLine("Dear {0:G},", person);
            System.Console.WriteLine("Dear {0:P},", person);
            System.Console.WriteLine("Dear {0:I},", person);
            System.Console.WriteLine("Dear {0},", person);
            System.Console.WriteLine("Dear {0:S},", person);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Usage

When executed, the preceding example produces the following output:

Dear Richard Peters,

Dear Mr. R. G. D. Peters,

Dear Richard,

Dear Richard Peters,

Dear R. Peters,

Implement a Custom Exception Class

Problem

You need to create a custom exception class so that you can use the runtime's exception-handling mechanism to handle application-specific exceptions.

Solution

Create a serializable class that extends the System.Exception class. Add support for any custom data members required by the exception, including constructors and properties required to manipulate the data members.

How It Works

Exception classes are unique in the fact that you do not declare new classes solely to implement new or extended functionality. The runtime's exception-handling mechanism—exposed by the C# statements try, catch, and finally—works based on the type of exception thrown, not the functional or data members implemented by the thrown exception.

If you need to throw an exception, you should use an existing exception class from the .NET Framework class library, if a suitable one exists. For example, some useful exceptions include the following:

  • System.ArgumentNullException, when code passes a null argument value that does not support null arguments to your method

  • System.ArgumentOutOfRangeException, when code passes an inappropriately large or small argument value to your method

  • System.FormatException, when code attempts to pass your method a String argument containing incorrectly formatted data

If none of the existing exception classes meet your needs, or you feel your application would benefit from using application-specific exceptions, it's a simple matter to create your own exception class. In order to integrate your custom exception with the runtime's exception-handling mechanism and remain consistent with the pattern implemented by .NET Framework–defined exception classes, you should do the following:

  • Give your exception class a meaningful name ending in the word Exception, such as TypeMismatchException or RecordNotFoundException.

  • Mark your exception class as sealed if you do not intend other exception classes to extend it.

  • Implement additional data members and properties to support custom information that the exception class should provide.

  • Implement three public constructors with the signatures shown here and ensure that they call the base class constructor:

    public CustomException() : base() {}
    public CustomException(string msg): base(msg) {}
    public CustomException(string msg, Exception inner) : base(msg, inner) {}
  • Make your exception class serializable so that the runtime can marshal instances of your exception across application domain and machine boundaries. Applying the attribute System.SerializableAttribute is sufficient for exception classes that do not implement custom data members. However, because Exception implements the interface System.Runtime.Serialization.ISerializable, if your exception declares custom data members, you must override the ISerializable.GetObjectData method of the Exception class as well as implement a deserialization constructor with this signature. If your exception class is sealed, mark the deserialization constructor as private; otherwise, mark it as protected. The GetObjectData method and deserialization constructor must call the equivalent base class method to allow the base class to serialize and deserialize its data correctly. (See recipe 13-1 for details on making classes serializable.)

Tip

In large applications, you will usually implement quite a few custom exception classes. It pays to put significant thought into how you organize your custom exceptions and how code will use them. Generally, avoid creating new exception classes unless code will make specific efforts to catch that exception; use data members to achieve informational granularity, not additional exception classes. In addition, avoid deep class hierarchies when possible in favor of broad, shallow hierarchies.

The Code

The following example is a custom exception named CustomException that extends Exception and declares two custom data members: a string named stringInfo and a bool named booleanInfo.

using System;
using System.Runtime.Serialization;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // Mark CustomException as Serializable.
    [Serializable]
    public sealed class CustomException : Exception
    {
        // Custom data members for CustomException.
        private string stringInfo;
        private bool booleanInfo;

        // Three standard constructors and simply call the base class.
        // constructor (System.Exception).
        public CustomException() : base() { }

        public CustomException(string message) : base(message) { }

        public CustomException(string message, Exception inner)
            : base(message, inner) { }

        // The deserialization constructor required by the ISerialization
        // interface. Because CustomException is sealed, this constructor
        // is private. If CustomException were not sealed, this constructor
        // should be declared as protected so that derived classes can call
        // it during deserialization.
        private CustomException(SerializationInfo info,
            StreamingContext context) : base(info, context)
        {
            // Deserialize each custom data member.
            stringInfo = info.GetString("StringInfo");
            booleanInfo = info.GetBoolean("BooleanInfo");
        }

        // Additional constructors to allow code to set the custom data
        // members.
        public CustomException(string message, string stringInfo,
            bool booleanInfo) : this(message)
        {
            this.stringInfo = stringInfo;
            this.booleanInfo = booleanInfo;
        }
public CustomException(string message, Exception inner,
            string stringInfo, bool booleanInfo): this(message, inner)
        {
            this.stringInfo = stringInfo;
            this.booleanInfo = booleanInfo;
        }

        // Read-only properties that provide access to the custom data members.
        public string StringInfo
        {
            get { return stringInfo; }
        }

        public bool BooleanInfo
        {
            get { return booleanInfo; }
        }

        // The GetObjectData method (declared in the ISerializable interface)
        // is used during serialization of CustomException. Because
        // CustomException declares custom data members, it must override the
        // base class implementation of GetObjectData.
        public override void GetObjectData(SerializationInfo info,
            StreamingContext context)
        {
            // Serialize the custom data members.
            info.AddValue("StringInfo", stringInfo);
            info.AddValue("BooleanInfo", booleanInfo);

            // Call the base class to serialize its members.
            base.GetObjectData(info, context);
        }

        // Override the base class Message property to include the custom data
        // members.
        public override string Message
        {
            get
            {
                string message = base.Message;
                if (stringInfo != null)
                {
                    message += Environment.NewLine +
                        stringInfo + " = " + booleanInfo;
                }
                return message;
            }
        }
    }
// A class to demonstrate the use of CustomException.
    public class Recipe13_08
    {
        public static void Main()
        {
            try
            {
                // Create and throw a CustomException object.
                throw new CustomException("Some error",
                    "SomeCustomMessage", true);
            }
            catch (CustomException ex)
            {
                Console.WriteLine(ex.Message);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement a Custom Event Argument

Problem

When you raise an event, you need to pass an event-specific state to the event handlers.

Solution

Create a custom event argument class derived from the System.EventArg class. When you raise the event, create an instance of your event argument class and pass it to the event handlers.

How It Works

When you declare your own event types, you will often want to pass event-specific state to any listening event handlers. To create a custom event argument class that complies with the Event pattern defined by the .NET Framework, you should do the following:

  • Derive your custom event argument class from the EventArgs class. The EventArgs class contains no data and is used with events that do not need to pass event state.

  • Give your event argument class a meaningful name ending in EventArgs, such as DiskFullEventArgs or MailReceivedEventArgs.

  • Mark your argument class as sealed if you do not intend other event argument classes to extend it.

  • Implement additional data members and properties that you need to pass to event handlers to support event state. It's best to make event state immutable, so you should use private readonly data members and public properties to provide read-only access to the data members.

  • Implement a public constructor that allows you to set the initial configuration of the event state.

  • Make your event argument class serializable so that the runtime can marshal instances of it across application domain and machine boundaries. Applying the attribute System.SerializableAttribute is usually sufficient for event argument classes. However, if your class has special serialization requirements, you must also implement the interface System.Runtime.Serialization.ISerializable. (See recipe 13-1 for details on making classes serializable.)

The Code

The following example demonstrates the implementation of an event argument class named MailReceivedEventArgs. Theoretically, an e-mail server passes instances of the MailReceivedEventArgs class to event handlers in response to the receipt of an e-mail message. The MailReceivedEventArgs class contains information about the sender and subject of the received e-mail message.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    [Serializable]
    public sealed class MailReceivedEventArgs : EventArgs
    {
        // Private read-only members that hold the event state that is to be
        // distributed to all event handlers. The MailReceivedEventArgs class
        // will specify who sent the received mail and what the subject is.
        private readonly string from;
        private readonly string subject;

        // Constructor, initializes event state.
        public MailReceivedEventArgs(string from, string subject)
        {
            this.from = from;
            this.subject = subject;
        }

        // Read-only properties to provide access to event state.
        public string From { get { return from; } }
        public string Subject { get { return subject; } }
    }
// A class to demonstrate the use of MailReceivedEventArgs.
    public class Recipe13_09
    {
        public static void Main()
        {
            MailReceivedEventArgs args =
                new MailReceivedEventArgs("Danielle", "Your book");

            Console.WriteLine("From: {0}, Subject: {1}", args.From, args.Subject);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Implement the Singleton Pattern

Problem

You need to ensure that only a single instance of a type exists at any given time and that the single instance is accessible to all elements of your application.

Solution

Implement the type using the Singleton pattern.

How It Works

Of all the identified patterns, the Singleton pattern is perhaps the most widely known and commonly used. The purposes of the Singleton pattern are to ensure that only one instance of a type exists at a given time and to provide global access to the functionality of that single instance. You can implement the type using the Singleton pattern by doing the following:

  • Implement a private static member within the type to hold a reference to the single instance of the type.

  • Implement a publicly accessible static property in the type to provide read-only access to the singleton instance.

  • Implement only a private constructor so that code cannot create additional instances of the type.

The Code

The following example demonstrates an implementation of the Singleton pattern for a class named SingletonExample:

using System;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    public class SingletonExample
    {
        // A static member to hold a reference to the singleton instance.
        private static SingletonExample instance;

        // A static constructor to create the singleton instance. Another
        // alternative is to use lazy initialization in the Instance property.
        static SingletonExample()
        {
            instance = new SingletonExample();
        }

        // A private constructor to stop code from creating additional
        // instances of the singleton type.
        private SingletonExample() { }

        // A public property to provide access to the singleton instance.
        public static SingletonExample Instance
        {
            get { return instance; }
        }

        // Public methods that provide singleton functionality.
        public void SomeMethod1() { /*..*/ }
        public void SomeMethod2() { /*..*/ }
    }
}

Usage

To invoke the functionality of the SingletonExample class, you can obtain a reference to the singleton using the Instance property and then call its methods. Alternatively, you can execute members of the singleton directly through the Instance property. The following code shows both approaches.

// Obtain reference to singleton and invoke methods
SingletonExample s = SingletonExample.Instance;
s.SomeMethod1();

// Execute singleton functionality without a reference
SingletonExample.Instance.SomeMethod2();

Implement the Observer Pattern

Problem

You need to implement an efficient mechanism for an object (the subject) to notify other objects (the observers) about changes to its state.

Solution

Implement the Observer pattern using delegate types as type-safe function pointers and event types to manage and notify the set of observers.

How It Works

The traditional approach to implementing the Observer pattern is to implement two interfaces: one to represent an observer (IObserver) and the other to represent the subject (ISubject). Objects that implement IObserver register with the subject, indicating that they want to be notified of important events (such as state changes) affecting the subject. The subject is responsible for managing the list of registered observers and notifying them in response to events affecting the subject. The subject usually notifies observers by calling a Notify method declared in the IObserver interface. The subject might pass data to the observer as part of the Notify method, or the observer might need to call a method declared in the ISubject interface to obtain additional details about the event.

Although you are free to implement the Observer pattern in C# using the approach just described, the Observer pattern is so pervasive in modern software solutions that C# and the .NET Framework include event and delegate types to simplify its implementation. The use of events and delegates means that you do not need to declare IObserver and ISubject interfaces. In addition, you do not need to implement the logic necessary to manage and notify the set of registered observers—the area where most coding errors occur.

The .NET Framework uses one particular implementation of the event-based and delegate-based Observer pattern so frequently that it has been given its own name: the Event pattern. (Pattern purists might prefer the name Event idiom, but Event pattern is the name most commonly used in Microsoft documentation.)

The Code

The example for this recipe contains a complete implementation of the Event pattern, which includes the following types:

  • Thermostat class (the subject of the example), which keeps track of the current temperature and notifies observers when a temperature change occurs

  • TemperatureChangeEventArgs class, which is a custom implementation of the System.EventArgs class used to encapsulate temperature change data for distribution during the notification of observers

  • TemperatureEventHandler delegate, which defines the signature of the method that all observers of a Thermostat object must implement and that a Thermostat object will call in the event of temperature changes

  • TemperatureChangeObserver and TemperatureAverageObserver classes, which are observers of the Thermostat class

The TemperatureChangeEventArgs class (in the following listing) derives from the class System.EventArgs. The custom event argument class should contain all of the data that the subject needs to pass to its observers when it notifies them of an event. If you do not need to pass data with your event notifications, you do not need to define a new argument class; simply pass EventArgs.Empty or null as the argument when you raise the event. (See recipe 13-9 for details on implementing custom event argument classes.)

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // An event argument class that contains information about a temperature
    // change event. An instance of this class is passed with every event.
    public class TemperatureChangedEventArgs : EventArgs
    {
        // Private data members contain the old and new temperature readings.
        private readonly int oldTemperature, newTemperature;

        // Constructor that takes the old and new temperature values.
        public TemperatureChangedEventArgs(int oldTemp, int newTemp)
        {
            oldTemperature = oldTemp;
            newTemperature = newTemp;
        }

        // Read-only properties provide access to the temperature values.
        public int OldTemperature { get { return oldTemperature; } }
        public int NewTemperature { get { return newTemperature; } }
    }
}

The following code shows the declaration of the TemperatureEventHandler delegate. Based on this declaration, all observers must implement a method (the name is unimportant) that returns void and takes two arguments: an Object instance as the first argument and a TemperatureChangeEventArgs object as the second. During notification, the Object argument is a reference to the Thermostat object that raises the event, and the TemperatureChangeEventArgs argument contains data about the old and new temperature values.

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // A delegate that specifies the signature that all temperature event
    // handler methods must implement.
    public delegate void TemperatureChangedEventHandler(Object sender,
        TemperatureChangedEventArgs args);
}

For the purpose of demonstrating the Observer pattern, the example contains two different observer types: TemperatureAverageObserver and TemperatureChangeObserver. Both classes have the same basic implementation. TemperatureAverageObserver keeps a count of the number of temperature change events and the sum of the temperature values, and displays an average temperature when each event occurs. TemperatureChangeObserver displays information about the change in temperature each time a temperature change event occurs.

The following listing shows the TemperatureChangeObserver and TemperatureAverageObserver classes. Notice that the constructors take references to the Thermostat object that the TemperatureChangeObserver or TemperatureAverageObserver object should observe. When you instantiate an observer, pass it a reference to the subject. The observer must create a delegate instance containing a reference to the observer's event-handler method. To register as an observer, the observer object must then add its delegate instance to the subject using the subject's public event member. This is made even easier with the simplified delegate syntax provided by C#, where it is no longer required to explicitly instantiate a delegate to wrap the listening method.

Once the TemperatureChangeObserver or TemperatureAverageObserver object has registered its delegate instance with the Thermostat object, you need to maintain a reference to this Thermostat object only if you want to stop observing it later on. In addition, you do not need to maintain a reference to the subject, because a reference to the event source is included as the first argument each time the Thermostat object raises an event through the TemperatureChange method.

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // A Thermostat observer that displays information about the change in
    // temperature when a temperature change event occurs.
    public class TemperatureChangeObserver
    {
        // A constructor that takes a reference to the Thermostat object that
        // the TemperatureChangeObserver object should observe.
        public TemperatureChangeObserver(Thermostat t)
        {
            // Create a new TemperatureChangedEventHandler delegate instance and
            // register it with the specified Thermostat.
            t.TemperatureChanged += this.TemperatureChange;
        }

        // The method to handle temperature change events.
        public void TemperatureChange(Object sender,
            TemperatureChangedEventArgs temp)
        {
            Console.WriteLine ("ChangeObserver: Old={0}, New={1}, Change={2}",
                temp.OldTemperature, temp.NewTemperature,
                temp.NewTemperature - temp.OldTemperature);
        }
    }

    // A Thermostat observer that displays information about the average
    // temperature when a temperature change event occurs.
    public class TemperatureAverageObserver
    {
// Sum contains the running total of temperature readings.
        // Count contains the number of temperature events received.
        private int sum = 0, count = 0;

        // A constructor that takes a reference to the Thermostat object that
        // the TemperatureAverageObserver object should observe.
        public TemperatureAverageObserver(Thermostat t)
        {
            // Create a new TemperatureChangedEventHandler delegate instance and
            // register it with the specified Thermostat.
            t.TemperatureChanged += this.TemperatureChange;
        }

        // The method to handle temperature change events.
        public void TemperatureChange(Object sender,
            TemperatureChangedEventArgs temp)
        {
            count++;
            sum += temp.NewTemperature;

            Console.WriteLine
                ("AverageObserver: Average={0:F}", (double)sum / (double)count);
        }
    }
}

Finally, the Thermostat class is the observed object in this Observer (Event) pattern. In theory, a monitoring device sets the current temperature by calling the Temperature property on a Thermostat object. This causes the Thermostat object to raise its TemperatureChange event and send a TemperatureChangeEventArgs object to each observer.

The example contains a Recipe13_11 class that defines a Main method to drive the example. After creating a Thermostat object and two different observer objects, the Main method repeatedly prompts you to enter a temperature. Each time you enter a new temperature, the Thermostat object notifies the listeners, which display information to the console. The following is the code for the Thermostat class:

namespace Apress.VisualCSharpRecipes.Chapter13
{
    // A class that represents a Thermostat, which is the source of temperature
    // change events. In the Observer pattern, a Thermostat object is the
    // subject that Observers listen to for change notifications.
    public class Thermostat
    {
        // Private field to hold current temperature.
        private int temperature = 0;

        // The event used to maintain a list of observer delegates and raise
        // a temperature change event when a temperature change occurs.
        public event TemperatureChangedEventHandler TemperatureChanged;

        // A protected method used to raise the TemperatureChanged event.
        // Because events can be triggered only from within the containing
// type, using a protected method to raise the event allows derived
        // classes to provide customized behavior and still be able to raise
        // the base class event.
        virtual protected void OnTemperatureChanged
            (TemperatureChangedEventArgs args)
        {
            // Notify all observers. A test for null indicates whether any
            // observers are registered.
            if (TemperatureChanged != null)
            {
                TemperatureChanged(this, args);
            }
        }

        // Public property to get and set the current temperature. The "set"
        // side of the property is responsible for raising the temperature
        // change event to notify all observers of a change in temperature.
        public int Temperature
        {
            get { return temperature; }

            set
            {
                // Create a new event argument object containing the old and
                // new temperatures.
                TemperatureChangedEventArgs args =
                    new TemperatureChangedEventArgs(temperature, value);

                // Update the current temperature.
                temperature = value;

                // Raise the temperature change event.
                OnTemperatureChanged(args);
            }
        }
    }

    // A class to demonstrate the use of the Observer pattern.
    public class Recipe13_11
    {
        public static void Main()
        {
            // Create a Thermostat instance.
            Thermostat t = new Thermostat();

            // Create the Thermostat observers.
            new TemperatureChangeObserver(t);
            new TemperatureAverageObserver(t);
// Loop, getting temperature readings from the user.
            // Any noninteger value will terminate the loop.
            do
            {
                Console.WriteLine(Environment.NewLine);
                Console.Write("Enter current temperature: ");

                try
                {
                    // Convert the user's input to an integer and use it to set
                    // the current temperature of the Thermostat.
                    t.Temperature = Int32.Parse(Console.ReadLine());
                }
                catch (Exception)
                {
                    // Use the exception condition to trigger termination.
                    Console.WriteLine("Terminating Observer Pattern Example.");

                    // Wait to continue.
                    Console.WriteLine(Environment.NewLine);
                    Console.WriteLine("Main method complete. Press Enter");
                    Console.ReadLine();
                    return;
                }
            } while (true);
        }
    }
}

Usage

The following listing shows the kind of output you should expect if you build and run the previous example. The bold values show your input.

Enter current temperature: 50

ChangeObserver: Old=0, New=50, Change=50

AverageObserver: Average=50.00


Enter current temperature: 20

ChangeObserver: Old=50, New=20, Change=-30

AverageObserver: Average=35.00
Enter current temperature: 40

ChangeObserver: Old=20, New=40, Change=20

AverageObserver: Average=36.67

Implement a Parallel Producer-Consumer Pattern

Problem

You need to coordinate several threads using a collection, such that one or more producer threads places items into the collection as one or more consumer threads removes items from it.

Solution

Use the System.Collections.Concurrent.BlockingCollection class.

How It Works

The BlockingCollection is a wrapper class that provides the foundation for the parallel producer-consumer pattern. Consumer threads are blocked when trying to take data from the collection until there are data items available. Optionally, producer threads are blocked when trying to add data to the collection if there are too many items already in the collection.

BlockingCollection wraps around collections classes that implement the System.Collections.Concurrent.IProducerConsumerCollection interface—this includes the ConcurrentQueue, ConcurrentStack, and ConcurrentBag collections in the System.Collections.Concurrent namespace.

To create a new instance of BlockingCollection, pass in an instance of the collection that you want to wrap and, if required, the maximum number of items you wish to be in the collection before producers will block when adding. For example, the following statement creates a new instance wrapped around a ConcurrentQueue with a size limit of three pending items:

new BlockingCollection<string>(new ConcurrentStack<string>(), 3);

The default constructor for BlockingCollection (which takes no arguments) uses the ConcurrentQueue class as the underlying collection, and uses no size limit—meaning that items will be taken out of the collection in the same order in which they were added, and also that producer threads will not block when adding items, irrespective of how many items are in the collection.

There are two ways for consumers to take items out of the collection. If you have one consumer thread, then the simplest way is to call the GetConsumingEnumerable method and use the resulting IEnumerable in a foreach loop. The loop will block when there are no items in the collection to be consumed. If you have multiple consumers, then they should call the Take method, which will return an item if one is available in the collection or block until such time as one becomes available.

If you don't want your producers and consumers to block, you can use the BlockingCollection.TryAdd and BlockingCollection.TryTake methods. These methods won't block, regardless of the state of the collection, and they return a bool to indicate whether the add or take operations succeeded.

When using this pattern, there often comes a point when your producers have added all of the items that you require and their tasks or threads have completed. However, your consumers will still be blocking because they continue to wait for new items to arrive in the collection. To avoid this situation, you should call the BlockingCollection.CompleteAdding instance method, which stops the methods the consumers are using from blocking—see the following code example for an illustration of this.

The Code

The following example creates a BlockingCollection using a ConcurrentQueue as the underlying collection. Using the .NET parallel programming features (see Chapter 15 for further information about parallel programming), a single consumer reads items from the collection while four producers add items. The main application thread waits for the producers to add their items and finish, before calling CompleteAdding on the collection. This causes the consumer's foreach method to stop blocking when all of the items are read from the collection.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    class Recipe13_12
    {
        static void Main(string[] args)
        {
            // Create the collection.
            BlockingCollection<string> bCollection
                = new BlockingCollection<string>(new ConcurrentQueue<string>(), 3);

            // Start the consumer.
            Task consumerTask = Task.Factory.StartNew(
                () => performConsumerTasks(bCollection));
            // Start the producers.
            Task producer0 = Task.Factory.StartNew(
                () => performProducerTasks(bCollection, 0));
            Task producer1 = Task.Factory.StartNew(
                () => performProducerTasks(bCollection, 1));
            Task producer2 = Task.Factory.StartNew(
                () => performProducerTasks(bCollection, 2));
            Task producer3 = Task.Factory.StartNew(
                () => performProducerTasks(bCollection, 3));

            // Wait for the producers to complete.
            Task.WaitAll(producer0, producer1, producer2, producer3);
            Console.WriteLine("Producer tasks complete.");
// Indicate that we will not add anything further.
            bCollection.CompleteAdding();

            // Wait for the consumer task to complete.
            consumerTask.Wait();

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }

        static void performConsumerTasks(BlockingCollection<string> collection)
        {
            Console.WriteLine("Consumer started");
            foreach (string collData in collection.GetConsumingEnumerable())
            {
                // Write out the data.
                Console.WriteLine("Consumer got message {0}", collData);
            }
            Console.WriteLine("Consumer completed");
        }

        static void performProducerTasks(BlockingCollection<string> collection,
            int taskID)
        {
            Console.WriteLine("Producer started");
            for (int i = 0; i < 100; i++)
            {
                // Put something into the collection.
                collection.Add("TaskID " + taskID + " Message" + i++);
                Console.WriteLine("Task {0} wrote message {1}", taskID, i);
            }
        }
    }
}

Perform Lazy Object Initialization

Problem

You want to defer instantiating an object until it is used for the first time.

Solution

Use the System.Lazy class to wrap the creation of your data type and call the Lazy.Value instance method to access the type instance—the type will not be initialized until Lazy.Value is first called.

How It Works

The .NET Framework performs eager initialization by default, which means that types are initialized as soon as they are created. By contrast, lazy initialization lets you defer object initialization until you need to access one of the members of your type. Eager initialization tends to create applications that create lots of objects when they start, even though the objects themselves may not be used for some time—this can consume resources unnecessarily and slow down your application, at least until all of the objects are created. To use lazy initialization, you simply pass your normal object instantiation as a delegate argument to the constructor of the System.Lazy class, so that

MyDataType mytype = new MydataType();

becomes

Lazy<MyDataType> myType = new Lazy<MyDataType<(() => new MyDataType());

In order to access the type within the Lazy instance, you call the Value property—the first time that Value is called, the type will be initialized—in this way, you can defer initializing your object until you need to use it.

The Code

The following example defines a type called MyDataType, which has a constructor and a method called sayHello. The Main method called when the application starts creates an instance of MyDataType using eager initialization, prints out a message simulating performing other tasks, and then calls sayHello. This process is then repeated using lazy initialization. The result is that the constructor is called as soon as the object reference is created using eager initialization, whereas the constructor is not called until the sayHello method is invoked when using lazy initialization.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    class Recipe13_13
    {
        static void Main(string[] args)
        {
            // Create an instance using eager initialization.
            MyDataType eagerInstance = new MyDataType(false);
            Console.WriteLine("...do other things...");
            eagerInstance.sayHello();

            Lazy<MyDataType> lazyInstance = new Lazy<MyDataType>(()
               => new MyDataType(true));
            Console.WriteLine("...do other things...");
            lazyInstance.Value.sayHello();
// Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }

    class MyDataType
    {
        public MyDataType(bool lazy)
        {
            Console.WriteLine("Initializing MyDataType - lazy instance: {0}", lazy);
        }

        public void sayHello()
        {
            Console.WriteLine("MyDataType Says Hello");
        }
    }
}

Use Optional Parameters

Problem

You need to define a method with optional parameters.

Solution

Supply a default value for the parameters you wish to make optional when defining your method.

How It Works

The optional parameters feature allows you to simplify a common programming pattern, where several slightly different methods exist to allow a caller to use default values, such as the following:

void printMessage()
{
    printMessage("Adam");
}

void printMessage(string from)
{
    printMessage(from, "Hello");
}
void printMessage(string from, string message)
{
    printMessage(from, message, false);
}

void printMessage(string from, string message, bool urgent)
{
    // Do something.
}

This approach allows callers of the method to rely on default values—this helps to simplify the code of the calling classes. C# supports optional parameters so that you can achieve the same effect with only one method in your class—you do this by setting the default values when defining the parameters—for example:

void printMessage(string from = "Adam", string message = "Hello",
    bool urgent = false)

Optional parameters must be defined after normal parameters.

The Code

The following example defines a method with three optional parameters:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    class Recipe13_14
    {
        static void Main(string[] args)
        {
            printMessage();
            printMessage("Allen");
            printMessage("Bob", "Goodbye");
            printMessage("Joe", "Help", true);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }

        static void printMessage(string from = "Adam", string message = "Hello",

bool urgent = false)

        {
Console.WriteLine("From: {0}, Urgent: {1}, Message: {2}",
                from, message, urgent);
        }
    }
}

Add a Method to a Type Without Modifying It

Problem

You want to add a method to a type without modifying it, most likely because you didn't write the type you want to modify.

Solution

Implement and call a custom extension method.

How It Works

Extension types allow you to extend a type by providing new methods in a separate class file and associating them with the type you wish to apply them to. The main need for this C# feature is when you want to associate new features with a type that you didn't write—one from the .NET Framework class library, for example. To create an extension method, start by creating a static class—a static class has the keyword static before class in the declaration. A static class is like a regular class, except the class cannot be instantiated and all of the methods must be static.

Add a static method to the static class with the name and result type you require. The first parameter of the method must be of the type that you wish to extend and be prefaced with the word this.

Implement the method body, performing the tasks you require. The parameter you prefaced with this represents the instance of the type you have extended on which your method has been invoked. For example, suppose we define an extension for string like this:

public static int countVowels(this string str)

The str parameter is the string instance that the extension has been invoked to process. To use an extension method, you must ensure that the namespace in which you created the static class is available to the calling class with the using keyword, just as you would for any other namespace. Then you simply call the extension method as though it is an instance method of the type you have extended—for example:

String mystring = "Hello";
Mystring.countVowels();

Note that you don't need to provide the first argument you declared for your extension method (the one prefaced with this).

The Code

The following example defines two extension methods for the string type in the namespace Apress.VisualCSharpRecipes.Chapter13.Extensions:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Apress.VisualCSharpRecipes.Chapter13.Extensions;

namespace Apress.VisualCSharpRecipes.Chapter13.Extensions
{
    public static class MyStringExtentions
    {
        public static string toMixedCase(this String str)
        {
            StringBuilder builder = new StringBuilder(str.Length);
            for (int i = 0; i < str.Length; i += 2)
            {
                builder.Append(str.ToLower()[i]);
                builder.Append(str.ToUpper()[i + 1]);
            }
            return builder.ToString();
        }

        public static int countVowels(this String str)
        {
            char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
            int vowelcount = 0;
            foreach (char c in str)
            {
                if (vowels.Contains(c))
                {
                    vowelcount++;
                }
            }
            return vowelcount;
        }
    }
}

namespace Apress.VisualCSharpRecipes.Chapter13
{
    class Recipe13_15
    {
        static void Main(string[] args)
        {
            string str = "The quick brown fox jumped over the...";
            Console.WriteLine(str.toMixedCase());
            Console.WriteLine("There are {0} vowels", str.countVowels());
// Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter");
            Console.ReadLine();
        }
    }
}

Call an Object Member Dynamically

Problem

You need to call a method or property dynamically.

Solution

Use the dynamic keyword to disable static checking for an object instance.

How It Works

Usually, the C# compiler checks to see that calls you make to type members are valid—that they exist, that they are accessible to the type you are calling from, that you have supplied the right number of arguments, that the arguments are of the right type, and so on.

C# also supports dynamic calls to type members in which these checks are not performed until the program is running and the call needs to be made. In order to take advantage of this feature, you declare an instance of dynamic—for example:

dynamic myobject = new MyObject();

You can then use the object reference you have created as you would normally—however, the calls you make against the dynamic instance are not checked by the compiler, and errors will not be detected until the calls are executed at runtime.

The Code

The following example defines a type. The program Main method creates a dynamic instance of that type and calls countVowels, which exists, and thisMethodDoesNotExist, which does not.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
namespace Apress.VisualCSharpRecipes.Chapter13
{
    class myType
    {
        public myType(string strval)
        {
            str = strval;
        }

        public string str {get; set;}

        public int countVowels()
        {
            char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
            int vowelcount = 0;
            foreach (char c in str)
            {
                if (vowels.Contains(c))
                {
                    vowelcount++;
                }
            }
            return vowelcount;
        }
    }

    class Recipe13_16
    {
        static void Main(string[] args)
        {
            // create a dynamic type
            dynamic dynInstance
                = new myType("The quick brown fox jumped over the...");
            // call the countVowels method
            int vowels = dynInstance.countVowels();
            Console.WriteLine("There are {0} vowels", vowels);
            // call a method that does not exist
            dynInstance.thisMethodDoesNotExist();
        }
    }
}

The code compiles, even though we have called a method that does not exist. When we execute the program, we get the following output:

There are 10 vowels



Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:

'Apress.VisualCSharpRecipes.Chapter13.myType

' does not contain a definition for 'thisMethodDoesNotExist'

at CallSite.Target(Closure , CallSite , Object )

at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid1[T0](

CallSite site, T0 arg0)

at Apress.VisualCSharpRecipes.Chapter13.Recipe13_16.Main(String[] args) in

C:UsersAdamDocumentsWorkC# CookbookChapter13Recipe13-16

Recipe13-16.cs:line 44

Press any key to continue . . .

Create a Variant Generic Type

Problem

You need to treat a generic interface of one type as a generic interface of a type it derives from.

Solution

Apply the in or out modifiers to your generic interface definition.

How It Works

Generic types allow you to provide strict controls on types that collections and classes will accept, but can create some unexpected behaviors. For example, suppose we define two classes, one of which derives from the other, and a generic interface, as follows:

class BaseType
{
}

class DerivedType : BaseType
{
}

public interface IMyInterface<T>
{

T getValue();

}

The following program illustrates the problem—the compiler won't allow us to treat a IMyInterface<DerivedType> as a IMyInterface<BaseType> so that we can call the processData method:

class Recipe13_17
    {
        static void Main(string[] args)
        {
            IMyInterface<DerivedType> variant = // implementation class //;
            processData(variant);
        }

        static void processData(IMyInterface<BaseType> data)
        {
            ...do something...
        }
    }

Covariance allows you to change this behavior when there is no possibility of breaking type safety. You use covariance by applying the out keyword to your interface definition, such as

public interface IMyInterface<out T>

Now the preceding code will work. Covariance can only be used for interfaces that only contain methods that return the generic type—if you define a method that accepts the type as a parameter, the compiler will generate an error. Contravariance is the complement to covariance—in order to handle parameters, you must use the in keyword, either in a separate interface or as a different type in the same interface—for example:

public interface IMyInterface<out T1, in T2 >

Note

The generic interfaces in the .NET Framework class library have been updated in .NET 4.0 to use variance so that you can perform safe conversion using those types.

The Code

The following example is similar to the fragments shown preceding, and contains two types (one derived from the other) and a covariant generic interface. In the Main method, we create an implementation of the interface that is typed using the derived class, and then call a method that requires an implementation of the interface that is typed using the base class.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13
{
    class Recipe13_17
    {
        static void Main(string[] args)
        {
            // Create an implementation of the interface that is typed
            // (and contains) the derived type.
            IMyInterface<DerivedType> variant
                = new ImplClass<DerivedType>(new DerivedType());
            // Call a method that accepts an instance of the interface typed.
            // with the base type - if the interface has been
            // defined with the out keyword
            // This will work; otherwise, the compiler will report an error.m
            processData(variant);
        }

        static void processData(IMyInterface<BaseType> data)
        {
            data.getValue().printMessage();
        }
    }

    class BaseType
    {
        public virtual void printMessage()
        {
            Console.WriteLine("BaseType Message");
        }
    }

    class DerivedType : BaseType
    {
        public override void printMessage()
        {
            Console.WriteLine("DerivedType Message");
        }
    }
public interface IMyInterface<out T>
    {
        T getValue();
    }

    public class ImplClass<T> : IMyInterface<T>
    {
        private T value;
        public ImplClass(T val)
        {
            value = val;
        }

        public T getValue()
        {
            return value;
        }
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.183.152