Chapter 9. Generics

Generics are a way to declare types, interfaces, delegates, and methods in a type-agnostic way. For example, IComparable<T> defines an interface that specifies a comparison between objects of type T. The T is defined by you where needed.

One of the most common ways to use generics is in collection classes. Before generics, the ArrayList was commonly used to store a dynamic array of objects. ArrayList stored references to objects, so you had to always cast the items to the right type when pulling them out. Not so with generics. This topic deserves a chapter all its own (see Chapter 10, “Collections”), but is introduced here in the first section since it’s by far the most common scenario.

The rest of this chapter shows the various ways to use generics, not specifically in collections.

Create a Generic List

Solution: Use List<T> to create a list of objects of type T, as in this example:

image

Note

You should always default to using the generic collections rather than the older ArrayList-style collections from early versions of .NET.

See Chapter 10 for more information on generic collections of all types in .NET.

Create a Generic Method

Solution: Generics are specified with angle brackets that include the type name (usually T). T can then be used wherever a real type name is used, as in the following example:

image

The following output is produced:

image

Create a Generic Interface

Solution: Generics are commonly used on interfaces where the methods are defined with respect to a specific type, as in the following example:

image

Create a Generic Class

Solution: This is similar to the declaration for interfaces. As an example, consider this indexing class with a (very) naive implementation for brevity:

image

image

image

Create a Generic Delegate

Solution: Delegates are like type-safe function pointers, and those types can also be generic.

image

image

Use Multiple Generic Types

Solution: Separate the types by commas within the brackets, as in this example:

image

Note

Type parameters are sometimes labeled starting with T and using the alphabet from there on (there should not usually be more than a few type parameter arguments). However, you can also label them according to purpose. For example, Dictionary<TKey, TValue>.

Constrain the Generic Type

Solution: By adding certain constraints to the type, you are allowed to do more with it since the compiler can infer addition information. For example, by telling C# that T must be a reference type, you can assign null to an instance of type T.

To add a constraint, you add a where clause after the declaration, as in the examples in the following sections.

Constrain to a Reference Type

The class keyword is used to denote a reference type, as in this example:

image

Constrain to a Value Type

Sometimes, you’ll want to restrict interfaces or collections—for example, to value types only. You can do this with the struct keyword, as in this example:

image

Note that even though the syntax says struct, any value type, including the built-in ones, is allowed.

Constrain to an Interface or Base Class

You can constrain to any type, whether it’s an interface, base class, or a class itself. Anything that is that type or derived from that type will be valid. By doing this, the compiler allows you to call methods defined on the specified type. Here’s an example:

image

Constrain to a Type with a Default Constructor

If you need to create a new object of the unknown, generic type, you must tell the compiler that the type is restricted to those with a default (parameterless) constructor. You do this with a special usage of the new keyword, as shown here:

image

Note

Creation of generic types can be problematic in some cases. You may find yourself needing to use factory methods to create the type instead of new:

image

Have Multiple Constraints

You can separate multiple constraints with commas. Note that, if present, new() must appear last in the list.

image

Constrain Multiple Type Arguments

You can apply constraints to as many type arguments as you desire.

image

Convert IEnumerable<string> to IEnumerable<object> (Covariance)

Solution: In .NET 4, covariance and contravariance are supported on interfaces and delegates, so the preceding code will work as-is. This is known as covariance. It only works because IEnumerable is declared as IEnumerable<out T>, which means that you cannot give T as an input to any method on IEnumerable. This rule implies that any output of this collection can also be treated as a collection of the parent class of T, which makes sense since iterating over IRectangle is equivalent to iterating over IShape.

On the other hand, imagine if we had another type of shape, ICircle, which is also derived from IShape. The collection should not be treated as a collection of IShape for the purposes of insertion, because this would imply you could also add an ICircle, which is not the case, since it’s really a collection of IRectangle objects. That’s why T is declared as an out parameter: It only works when reading values out of the collection, not putting them in, and it explains why covariance can work in the preceding code example.

Convert IComparer<Child> to IComparer<Parent> (Contravariance)

Solution: In .NET 4, IComparer<T> has been changed to IComparer<in T>, which means that objects of type T are used only as input parameters. Therefore, an object implementing this interface can be assigned to interfaces of a more derived type. This is called contravariance. The preceding code sample will now work.

For more on contravariance and to see how it applies to delegates, see Chapter 15.

Create Tuples (Pairs and More)

Solution: Rather than creating your own class to handle tuples on a case-by-case basis, use the Tuple class:

var name = Tuple.Create("Ben", "Michael", "Watson");
string firstName = name.Item1;

Tuple values can be different types:

image

There are Create methods from 1 to 8 arguments (singleton to octuple).

..................Content has been hidden....................

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