Share via


Overview of Generics in the .NET Framework 

This topic provides an overview of the following capabilities of generics in the .NET Framework and the common language runtime:

  • A summary of generic types and methods and the terminology for talking about them.

  • Generic collection classes in the System.Collections.Generic and System.Collections.ObjectModel namespaces.

  • Other generic types.

  • Generic delegates for conversions, search predicates, and actions to be taken on elements of an array or collection.

  • Generic interfaces to provide common functionality across families of generic types.

What Are Generics, and How Are They Used and Defined?

Generics are classes, structures, interfaces, and methods that have placeholders (type parameters) for one or more of the types they store or use. A generic collection class might use a type parameter as a placeholder for the type of objects it stores; the type parameters appear as the types of its fields, and the parameter types of its methods. A generic method might use its type parameter as the type of its return value, or as the type of one of its formal parameters. The following code illustrates a simple generic class definition.

Public Class Generic(Of T)
    Public Field As T
End Class
public class Generic<T> 
{
    public T Field;
}
generic<typename T> public ref class Generic 
{
public:
    T Field;
};

When you create an instance of a generic class, you specify the actual types to substitute for the type parameters. This establishes a new generic class, referred to as a constructed generic class, with your chosen types substituted everywhere that the type parameters appear. The result is a type-safe class tailored to your choice of types, as the following code illustrates.

Dim g As New Generic(Of String)
g.Field = "A string"
Generic<string> g = new Generic<string>();
g.Field = "A string";
Generic<String^>^ g = gcnew Generic<String^>();
g->Field = "A string";

The following terms are used to talk about generics in the .NET Framework:

  • A generic type definition is a class, structure, or interface declaration that functions as a template, with placeholders for the types it can contain or use. For example, the Dictionary class can contain two types: keys and values. Because it is only a template, you cannot create instances of a class, structure, or interface that is a generic type definition.

  • Generic type parameters, or type parameters, are the placeholders in a generic type or method definition. The Dictionary generic type has two type parameters, TKey and TValue, representing the types of its keys and values.

  • A constructed generic type, or constructed type, is the result of specifying types for the generic type parameters of a generic type definition.

  • A generic type argument is any type that is substituted for a generic type parameter.

  • The general term "generic type" includes both constructed types and generic type definitions.

  • Constraints are limits placed on generic type parameters. For example, you might limit a type parameter to types that implement the IComparer generic interface, to ensure that instances of the type can be ordered. You can also constrain type parameters to types that have a particular base class, that have a default constructor, or that are reference types or value types. Users of the generic type cannot substitute type arguments that do not satisfy the constraints.

  • A generic method definition is a method with two parameter lists: a list of generic type parameters, and a list of formal parameters. Type parameters can appear as the return type or as the types of the formal parameters, as in the following code.

    Function Generic(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        ...
    End Function
    
    T Generic<T>(T arg) { T temp = arg; ...}
    
    generic<typename T> T Generic(T arg) { T temp = arg; ...};
    

    Generic methods can appear on generic or nongeneric types. It is important to note that a method is not generic just because it belongs to a generic type, or even because it has formal parameters whose types are the generic parameters of the enclosing type. A method is generic only if it has its own list of type parameters. In the following code, only method G is generic.

    Class A
        Function G(Of T)(ByVal arg As T) As T
            ...
        End Function
    End Class
    Class Generic(Of T)
        Function M(ByVal arg As T) As T
            ...
        End Function
    End Class
    
    class A 
    {
        T G<T>(T arg) {...}
    }
    class Generic<T> 
    {
        T M(T arg) {...}
    }
    
    ref class A 
    {
        generic<T> T G(T arg) {...};
    };
    generic<typename T> ref class Generic 
    {
        T M(T arg) {...};
    };
    

Visual C++, C#, and Visual Basic all provide full support for defining and consuming generics. For more information, see Generic Types in Visual Basic, Introduction to Generics (C# Programming Guide), and Overview of Generics in Visual C++.

Nested Types and Generics

A type nested in a generic type can depend on the type parameters of the enclosing generic type, and such nested types are considered generic by the common language runtime, even if they don't have generic type parameters of their own. When you create an instance of a nested type, it is necessary to specify type arguments for all enclosing generic types.

Generic Collections in the .NET Framework

A number of generic collection classes are provided in the .NET Framework class library, in the System.Collections.Generic and System.Collections.ObjectModel namespaces. For more information about these classes, see Commonly Used Collection Types.

System.Collections.Generic

Many of the generic collection types are direct analogs of nongeneric types. Dictionary is a generic version of Hashtable; it uses the generic structure KeyValuePair for enumeration, instead of DictionaryEntry.

List is a generic version of ArrayList. There are generic Queue and Stack classes corresponding to the nongeneric versions.

There are generic and nongeneric versions of SortedList, both of which are hybrids between a dictionary and a list, and which have similar performance characteristics. The SortedDictionary generic class is a pure dictionary that offers different performance characteristics, and which has no nongeneric counterpart.

The LinkedList generic class is a true linked list. It has no nongeneric counterpart.

System.Collections.ObjectModel

The Collection generic class provides a base class for deriving your own generic collection types. The ReadOnlyCollection class provides an easy way to produce a read-only collection from any type that implements the IList generic interface. The KeyedCollection generic class provides a way to store objects that contain their own keys.

Other Generic Types

The Nullable generic structure allows you to use value types as if they could be assigned null. This can be useful when working with database queries, where fields that contain value types can be missing. The generic type parameter can be any value type.

NoteNote

In C# it is not necessary to use Nullable explicitly, because the language has syntax for nullable types.

The ArraySegment generic structure provides a way to delimit a range of elements within a one-dimensional, zero-based array of any type. The generic type parameter is the type of the array's elements.

The EventHandler generic delegate eliminates the need to declare a delegate type to handle events, if your event follows the event-handling pattern used by the .NET Framework. For example, suppose you have created a MyEventArgs class, derived from EventArgs, to hold the data for your event. You can then declare the event as follows:

Public Event MyEvent As EventHandler(Of MyEventArgs)
public event EventHandler<MyEventArgs> MyEvent;
public:
    event EventHandler<MyEventArgs^>^ MyEvent;

Generic Delegates for Manipulating Arrays and Lists

The Action generic delegate represents a method that performs some action on an element of the specified type. You can create a method that performs the desired action on the element, create an instance of the Action delegate to represent that method, and then pass the array and the delegate to the System.Array.ForEach.Action{ static generic method. The method is called for every element of the array.

The List generic class also provides a ForEach method that uses the Action delegate. This method is not generic.

NoteNote

This makes an interesting point about generic types and methods. The System.Array.ForEach.Action{ method must be static (Shared in Visual Basic) and generic because Array is not a generic type; the only reason you can specify a type for System.Array.ForEach.Action{ to operate on is that the method has its own type parameter list. By contrast, the nongeneric System.Collections.Generic.List.ForEach(System.Action{ method belongs to the generic class List, so it simply uses the type parameter of its class. The class is strongly typed, so the method can be an instance method.

The Predicate generic delegate represents a method that determines whether a particular element meets criteria you define. You can use it with the following static generic methods of Array to search for an element or a set of elements: Exists, Find, FindAll, FindIndex, FindLast, FindLastIndex, and TrueForAll.

Predicate also works with the corresponding nongeneric instance methods of the List generic class.

The Comparison generic delegate allows you to provide a sort order for array or list elements that do not have a native sort order, or to override the native sort order. Create a method that performs the comparison, create an instance of the Comparison delegate to represent your method, and then pass the array and the delegate to the System.Array.Sort.Comparison{ static generic method. The List generic class provides a corresponding instance method overload, System.Collections.Generic.List.Sort(System.Comparison{.

The Converter generic delegate allows you to define a conversion between two types, and to convert an array of one type into an array of the other, or to convert a list of one type to a list of the other. Create a method that converts the elements of the existing list to a new type, create a delegate instance to represent the method, and use the System.Array.ConvertAll.Converter{ generic static method to produce an array of the new type from the original array, or the System.Collections.Generic.List.ConvertAll.Converter{ generic instance method to produce a list of the new type from the original list.

Chaining Delegates

Many of the methods that use these delegates return an array or list, which can be passed to another method. For example, if you want to select certain elements of an array, convert those elements to a new type, and save them in a new array, you can pass the array returned by the FindAll generic method to the ConvertAll generic method. If the new element type lacks a natural sort order, you can pass the array returned by the ConvertAll generic method to the Sort generic method.

Generic Interfaces

Generic interfaces provide type-safe counterparts to nongeneric interfaces for ordering and equality comparisons and for functionality shared by generic collection types.

Equality and Ordering Comparisons

In the System namespace, the System.IComparable and System.IEquatable generic interfaces, like their nongeneric counterparts, define methods for ordering comparisons and equality comparisons, respectively. Types implement these interfaces to provide the ability to perform such comparisons.

In the System.Collections.Generic namespace, the IComparer and IEqualityComparer generic interfaces offer a way define an ordering or equality comparison for types that do not implement the System.IComparable or System.IEquatable generic interface, and they provide a way to redefine those relationships for types that do. These interfaces are used by methods and constructors of many of the generic collection classes. For example, you can pass a generic IComparer to the constructor of the SortedDictionary class to specify a sort order for a type that does not implement generic System.IComparable. There are overloads of the System.Array.Sort generic static method and the System.Collections.Generic.List.Sort(System.Collections.Generic.IComparer{ instance method for sorting arrays and lists using generic IComparer implementations.

The Comparer and EqualityComparer generic classes provide base classes for implementations of the IComparer and IEqualityComparer generic interfaces, and also provide default ordering and equality comparisons through their respective System.Collections.Generic.Comparer.Default and System.Collections.Generic.EqualityComparer.Default properties.

Collection Functionality

The ICollection generic interface is the basic interface for generic collection types. It provides basic functionality for adding, removing, copying, and enumerating elements. ICollection inherits from both generic IEnumerable and nongeneric IEnumerable.

The IList generic interface extends the ICollection generic interface with methods for indexed retrieval.

The IDictionary generic interface extends the ICollection generic interface with methods for keyed retrieval. Generic dictionary types in the .NET Framework base class library also implement the nongeneric IDictionary interface.

The IEnumerable generic interface provides a generic enumerator structure. The IEnumerator generic interface implemented by generic enumerators inherits the nongeneric IEnumerator interface, and the MoveNext and Reset members, which do not depend on the type parameter T, appear only on the nongeneric interface. This means that any consumer of the nongeneric interface can also consume the generic interface.

Limitations of Generics

The following are some limitations of generics in the .NET Framework version 2.0:

  • Generic types can be derived from most base classes, such as MarshalByRefObject (and constraints can be used to require that generic type parameters derive from base classes like MarshalByRefObject), but in this release context-bound generic types are not supported. A generic type can be derived from ContextBoundObject, but attempting to create an instance of that type causes a TypeLoadException.

  • Enumerations cannot have generic type parameters. An enumeration can be generic only incidentally, for example because it is nested in a generic type defined using Visual Basic, C#, or C++. For more information, see Enumerations in the Common Type System.

  • Lightweight dynamic methods cannot be generic. For more information on dynamic methods, see Reflection Emit Dynamic Method Scenarios.

  • In Visual Basic, C#, and C++ a nested type enclosed in a generic type cannot be instantiated unless types have been assigned to the type parameters of all enclosing types. Another way of saying this is that in reflection, a nested type defined using these languages includes the type parameters of all its enclosing types. This allows the type parameters of enclosing types to be used in the member definitions of a nested type. For more information, see "Nested Types" in MakeGenericType.

    NoteNote

    A nested type that is defined by emitting code in a dynamic assembly or by using the MSIL Assembler (Ilasm.exe) is not required to include the type parameters of its enclosing types; however, if it does not then the type parameters are not in scope in the nested class.

    For more information, see "Nested Types" in MakeGenericType.

See Also

Tasks

How to: Define a Generic Type with Reflection Emit

Reference

Introduction to Generics (C# Programming Guide)
System.Collections.Generic
System.Collections.ObjectModel

Concepts

When to Use Generic Collections
Generic Types in Visual Basic
Overview of Generics in Visual C++

Other Resources

Commonly Used Collection Types
Reflection and Generic Types