Creating Custom Generic Classes


Now that you've seen how to use many of the generic classes offered by the .NET Framework, you can step into creating custom generic classes.

A generic class is defined similarly to a normal class with the generic type declaration. The generic type can then be used within the class as a field member, or with parameter types of methods:

 public class MyGeneric<T> { private T member; public void Method(T obj) { } } 

Every class that deals with the object type is a possible candidate for a generic implementation. Also, if classes make use of hierarchies, generics can be very helpful in making casting unnecessary.

Here you rework the DocumentManager class that was defined earlier. You can build this class with enough flexibility to not only support the Document type. The new version of this class is listed here:

 public class DocumentManager<T> { private readonly Queue<T> documentQueue = new Queue<T>(); public void AddDocument(T doc)    {       lock (this)       {          documentQueue.Enqueue(doc);       }    } public T GetDocument()    { T doc = default(T); lock (this)       {          doc = documentQueue.Dequeue();       }       return doc;    }    public bool IsDocumentAvailable    {       get       {          return (documentQueue.Count > 0) ? true : false;       }    } } 

The Document class has previously been used by the DocumentManager. Now the class is defined as a generic class that makes use of type T. The DocumentManager is flexible and can use any type for T. This type can be used with variables inside the class. As shown here, the generic type can also be used when using a generic type as Queue<T>. The same type that is used to instantiate the DocumentManager<T> class is used to instantiate the Queue<T> class:

public class DocumentManager<T> {    private readonly Queue<T> documentQueue = new Queue<T>();

The methods AddDocument() and GetDocument() have defined T as the parameter and return type:

   public void AddDocument(T doc)    {        //...    }        public T GetDocument()    {       //...    }

Default Values

It is not possible to assign null with generic types. The reason is that a generic type can also be instantiated as a value type, and null is allowed only with reference types. To circumvent this problem, you can use the default keyword. With the default keyword null is assigned to reference types and 0 is assigned to value types.

T doc = default(T);

Constraints

You have to do more to make the class ProcessDocuments generic. Because the class ProcessDocuments invokes methods of the Document class, a where clause must be defined to specify the methods and properties that must be available with the generic type. The where clause can require a generic type to implement an interface or to derive from a specific base class.

The new version of the class ProcessDocuments is made independent of the Document class and also independent of the DocumentManager. This is done by defining the interfaces IDocument and IDocumentManager<T>. These interfaces define the methods and properties that are used by ProcessDocuments<T, U>.

The interface IDocument defines the read-only properties Title and Content:

 public interface IDocument { string Title { get; } string Content { get; } } 

The previously created class Document now implements the interface IDocument:

 public class Document : IDocument {    //...

IDocumentManager<T> is a generic interface. Type T for the methods of this interface can be instantiated on a case-by-case basis:

 public interface IDocumentManager<T> { void AddDocument(T doc); T GetDocument(); bool IsDocumentAvailable { get; } } 

The class DocumentManager<T> now implements the interface IDocumentManager<T>. The generic type for the interface is defined by the class DocumentManager<T>:

 public class DocumentManager<T> : IDocumentManager<T> { 

The class ProcessDocuments now is changed to use two generic types: TDocument and TDocument Manager. The first where clause defines that the type TDocument must implement the interface IDocument. One type that can be used for TDocument is Document, because this class implements the interface IDocument. However, any other type that implements the interface IDocument can be used for TDocument. The where clause for the type TDocumentManager defines that this type must implement the interface IDocumentManager<TDocument>. So in case Document is used for TDocument, TDocumentManager must be a class that implements IDocumentManager<Document>.

The generic types TDocument and TDocumentManager are now used in the implementation:

 public class ProcessDocuments<TDocument, TDocumentManager> where TDocument : IDocument where TDocumentManager : IDocumentManager<TDocument> { public static void Start(TDocumentManager dm)    { new Thread( new ProcessDocuments<TDocument, TDocumentManager>(dm).Run).Start();    } protected ProcessDocuments(TDocumentManager dm)    {       documentManager = dm;    } private TDocumentManager documentManager;    protected void Run()    {       while (true)       {          if (documentManager.IsDocumentAvailable)          { T doc = documentManager.GetDocument();             Console.WriteLine("Processing document {0}", doc.Title);          }          Thread.Sleep(new Random().Next(20));       }    } }

In the Main() method the DocumentManager now is initiated by using the Document class. The static method Start() of the class ProcessDocuments is invoked by defining the Document class as a parameter for TDocument, and DocumentManager<Document> as the parameter for TDocumentManager:

static void Main(string[] args) { DocumentManager<Document> dm = new DocumentManager<Document>(); ProcessDocuments<Document, DocumentManager<Document>>.Start(dm);    for (int i = 0; i < 1000; i++)    {       Document doc = new Document("Doc " + i.ToString(), "content");       dm.AddDocument(doc);       Console.WriteLine("added document {0}", doc.Title);       Thread.Sleep(new Random().Next(20));    } }

In case new objects of a generic type must be instantiated in a generic class, the where clause can be extended with the constructor constraint new(). This constructor constraint defines that a default constructor must be available with the generic type:

 public class MyClass<T> where T : IFoo, new() { //... 
Note

With .NET 2.0 only constructor constraints for the default constructor can be defined. It is not possible to define a constructor constraint for other constructors.




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net