When creating generic classes, you might need some more C# keywords. For example, it is not possible to assign null to a generic type. In this case, the keyword default can be used. If the generic type.does not require the features of the Object class, but you need to invoke some specific methods in the generic class, you can define constraints.
This section discusses the following topics:
- Default Values
- Static members.
Let’s start this example with a generic document manager. The document manager is used to read and write documents from a queue. Start by creating a new Console project named DocumentManager and add the class DocumentManager<T>. The method AddDocument () adds a document to the queue. The read-only property IsDocumentAvailable returns true if the queue is not empty.
Now you add a GetDocument () method to the DocumentManager<T> class. Inside this method the type T should be assigned to null. However, it is not possible to assign null to 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.
The default keyword has multiple meanings depending on the context where it is used. The switch statement uses a default for defining the default case, and with generics the default is used to initialize generic types either to null or a depending on if it is a reference or value type.
If the generic class needs to invoke some methods from the generic type, you have to add constraints. With the DocumentManager<T>, all the titles of the documents should be displayed in the
DisplayAllDocuments () method.
The Document class implements the interface IDocument with the properties Title and Content:
The problem is that doing a cast results in a runtime exception if the type T does not implement the interface IDocument. Instead, it would be better to define a constraint with the DocumentManager <TDocument> class that the type TDocument must implement the interface lDocument:’: To clarify the requirement in the name of the generic type, T is changed to TDocument. The where clause defines the requirement to implement the interface lDocument:
public class DocumentManager<TDocument>
where TDocument : lDocument
This way you can write the foreach statement in such a way that the type T contains the property Ti tIe. You get support from VISual Studio IntelliSense and from the compiler:
public void DisplayAIIDocuments()
foreach (TDocument doc in documentQueue)
In the Main () method the DocumentManager<T> class is instantiated with the type Document that implements the required interface IDocument. Then new documents are added and displayed, and one of the documents is retrieved:
The DocumentManager now works. with any class that implements the interface IDocument. In the sample application, you’ve seen an interface constraint. Generics support several constraint types:
With CLR 2.0 only constructor constraints for the default constructor can be defined. It is not possible to define a constructor constraint for other constructors.
With a generic type, you can also combine multiple constraints. The constraint where T : IFoo , new () with the MyClass<T> declaration specifies that type T implements the interface IFoo and has a default constructor;
public class = MyClass<T>
where T : IFoo. new()
One important restriction of the where clause with C# is that its not possible to define operators that must be implemented by the generic type. Operator cannot be defined in interfaces. With the where cause, it is only possible to define base classes, interfaces, and the default constructor. .