HomeSearch

C# Generic Class, Generic Method Examples

Create and use generic classes and methods. See type parameter constraints and the where keyword.

Generics.

Generic classes have type parameters. Separate classes, each with a different field type in them, can be replaced with a single generic class.

A generic class

introduces a type parameter (often specified as the letter T). This becomes part of the class definition itself. Generic methods can also be designed.Type

Generic class example.

Now we specify a generic type. These types themselves contain type parameters, which influence the compilation of the code to use any type you specify.

Next: In this example, the letter T denotes a type that is only known based on the calling location.

And: The program can act upon the instance of T like it is a real type, but it is not.

C# program that describes generic class 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 method example.

Generic methods have type parameters. They provide a way to parameterize the types used in a method. One implementation is used with many different types.

Note: The syntax form for the declaration uses the <T> characters after the method name but before the formal parameter list.

Example: This program shows a generic method. The type parameter is customarily the type T or V or TValue or similar.

Also: This type is an open type. It is not precisely described in the program. Few assumptions about its representation can be made.

C# program that declares generic method using System; using System.Collections.Generic; class Program { static List<T> GetInitializedList<T>(T value, int count) { // This generic method returns a List with ten elements initialized. // ... It uses a type parameter. // ... It uses the "open type" T. List<T> list = new List<T>(); for (int i = 0; i < count; i++) { list.Add(value); } return list; } static void Main() { // Use the generic method. // ... Specifying the type parameter is optional here. // ... Then print the results. List<bool> list1 = GetInitializedList(true, 5); List<string> list2 = GetInitializedList<string>("Perls", 3); foreach (bool value in list1) { Console.WriteLine(value); } foreach (string value in list2) { Console.WriteLine(value); } } } Output True True True True True Perls Perls Perls

Notes, above program.

This program uses a generic method to construct a special List. The GetInitializedList member is the generic method and it uses a type parameter with name T.

Note: The first parameter to the GetInitializedList method is also a value of type T.

And: When you specify the type parameter, you can access the type parameter from other parts of the method signature as shown.

Generic methods.

Let us examine the C# language specification. It describes in precise detail all of the results for computing type inference matches.

Also: It describes the algorithm used by the language compiler. But these details are not useful for most programs.

Tip: Do not rely on language specification details in your program. Most developers do not study language specifications at length.

Generic methods, disambiguation.

Much complexity in generic methods involves disambiguation. This is the process by which the exact method you want to invoke is searched for and selected.

Note: You can often omit the type parameter, but not where the method call cannot be determined from the context.

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.

Tip: With constrained generic types, we can implement required functionality that depends on certain features.

Next: This program uses three classes that demonstrate different ways of constraining type parameters.

Info: The Ruby class requires that its type parameter implement IDisposable. We can thus call the Dispose method or using statement.

C# program that uses generic type constraints using System; using System.Data; /// <summary> /// Requires type parameter that implements interface IDisposable. /// </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>(); } }

Notes, above program.

The Python generic type demands that its type parameter be a struct. In C# integers (int) are structs so you could use Python<int> as the constructed type.Struct

Notes, continued.

The Perl type requires that its type parameter V be a reference type (a class). It also requires that the class has a public parameterless constructor.Constructor

Notes, Main.

The Ruby type was used with the DataTable type parameter, which implements IDisposable. The Python type was used with an int parameter.DataTable

Finally: The Perl type was used with the Program type, which has an implicit, public parameterless constructor.

Discussion.

Generic constrains are not often useful. If you are developing a complex framework (like the .NET Framework itself) they are necessary.

But: For a normal or even complex program, constraints are of limited application.

Tip: Typically, you do not need to constrain type parameters in your own generic types.

Discussion, continued.

We rarely need to make our own generic types now that the .NET Framework already contains so many useful ones. First, we should learn how to use prebuilt ones.

Terms.

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: This is another term for a generic type—it refers to a type with a type parameter.

Open type: This is also a term for a generic type. It is a type that has type parameters.

Closed type: As opposed to an open type, a closed type is one that does not have type parameters.

Unbound type: An unbound type is declared by a type declaration, but is not actually a type.

Bound type: A bound type is one of two types—a regular type or a constructed type. It is a type of types.

MultiMap.

This is a Dictionary (Map) that has many values at a single key. MultiMap allows fast lookup of Lists. The implementation here is not ideal, but uses the generic class syntax.MultiMapListDictionary

A summary.

Usually we will use generic types and methods that are provided by the .NET Framework. These include the Dictionary and List, and Array.IndexOf.Array.IndexOf, LastIndexOf
Home
Dot Net Perls
© 2007-2019 Sam Allen. All rights reserved. Written by Sam Allen, info@dotnetperls.com.