C# Generic Class

Squares

Generic classes have type parameters. Several separate classes in a C# program, each with a different field type in them, can be replaced with a single generic class. The generic class introduces a type parameter. This becomes part of the class definition itself.

This C# tutorial shows how to create and use generic classes.

Example

One of the most amazing features in the C# language is the ability to specify and use generic types. These types themselves contain type parameters, which influence the compilation of the code to use any type you specify. In this example, the letter T denotes a type that is only known based on the calling location; the program can act upon the instance of T like it is a real type, but it is not.

Program that describes generic class [C#]

using System;

class Test<T>
{
    T _value;

    public Test(T t)
    {
	// The field has the same type as the parameter.
	this._value = t;
    }

    public void Write()
    {
	Console.WriteLine(this._value);
    }
}

class Program
{
    static void Main()
    {
	// Use the generic type Test with an int type parameter.
	Test<int> test1 = new Test<int>(5);
	// Call the Write method.
	test1.Write();

	// Use the generic type Test with a string type parameter.
	Test<string> test2 = new Test<string>("cat");
	test2.Write();
    }
}

Output

5
cat

Generic class constraints. The C# language also provides ways for you to add more features to your generic types by reducing the range of types they can be parameterized with. This feature is not useful in many programs, but is occasionally useful—please see the next section.

Type parameter constraints

Steps

With constrained generic types, we can implement required functionality that depends on certain features. This program uses three classes that demonstrate different ways of constraining type parameters. The Ruby class requires that its type parameter implement IDisposable; this gives you the ability the call the Dispose method or using statement on the type parameter variable in the class—this is not shown.

Program that uses generic type constraints [C#]

using System;
using System.Data;

/// <summary>
/// Requires type parameter that implements interface IEnumerable.
/// </summary>
class Ruby<T> where T : IDisposable
{
}

/// <summary>
/// Requires type parameter that is a struct.
/// </summary>
class Python<T> where T : struct
{
}

/// <summary>
/// Requires type parameter that is a reference type with a constructor.
/// </summary>
class Perl<V> where V : class, new()
{
}

class Program
{
    static void Main()
    {
	// DataTable implements IDisposable so it can be used with Ruby.
	Ruby<DataTable> ruby = new Ruby<DataTable>();

	// Int is a struct (ValueType) so it can be used with Python.
	Python<int> python = new Python<int>();

	// Program is a class with a parameterless constructor (implicit)
	// ... so it can be used with Perl.
	Perl<Program> perl = new Perl<Program>();
    }
}

Result: It compiles correctly.

Python (struct). The Python generic type demands that its type parameter be a struct, or value type in general. Remember, in the C# language, integers (int) are considered to be structs so you could use Python<int> as the constructed type.

Perl (class, new()). Finally, the Perl type requires that its type parameter V be a reference type (a class) and also that the class has a public parameterless constructor. As a reminder, every class has a default public parameterless constructor unless you hide it.

Main method

Main method description. In the Main entry point we demonstrate that the program compiles correctly. The Ruby type was used with the DataTable type parameter, which implements IDisposable. The Python type was used with an int parameter. And finally the Perl type was used with the Program type, which has an implicit, public parameterless constructor.

Using DataTable BlockQuestion and answer

Is this useful? Not very often. If you are developing a complex framework—like the .NET Framework's classes themselves—it is necessary. If you are developing a normal or even complex program, it is of limited application. Typically, you do not need to constrain type parameters in your own generic types. Actually, I have found that you rarely need to make your own generic types now that the .NET Framework contains so many useful ones already.

Tip: It is a better use of your time to learn how to use the ones that were already built.

This section provides information

Thoughts. I think the type constraint system shown here is most useful for learning about the generic type design itself. The C# language provides a fascinating study of compiler and language design, and it is one of the most advanced in popular use. I consider generic types to be an amazingly useful and elegant—and fast—feature.

Review: Generic types can be constrained using the 'where' syntax in the C# programming language. Though not useful in every program, they help inform us as to the nature of generic types as well as provide features for generics, particularly those found in the Framework itself.

Terms

The C# Programming Language

In the C# specification, you will see lots of terms that apply to generic types. These terms include: open types, closed types, bound types, and unbound types.

Constructed type: A type with a type parameter.

Open type: A type that has type parameters.

Closed type: A type that does not have type parameters.

Unbound type: Declared by a type declaration, but not actually a type.

Bound type: A regular type or a constructed type.

Summary

.NET Framework information

Usually you will use generic types that are provided by the .NET Framework in your C# programs. These include the Dictionary and List types. But you can provide your own types. This ability is often reserved only for types that are used in many different ways, due to the complexity of implementation.

Class Examples
.NET