In C#, we can also have generic classes and generic methods i.e., classes and methods not made for a specific type but can be used with any general type.
We use <> brackets for this purpose. Suppose, we have defined a class or method with <T>
and performed each operation on T inside the method or the class. And we passed an integer while calling it – <int>
, then the T inside the class or the method will be changed to take int during the time of compilation.
Let’s look at generic classes first.
C# Generic Class Example
Let’s take an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System; class Generic<T> { private T genericVariable; public Generic(T genericValue) { this.genericVariable = genericValue; } public void Display() { Console.WriteLine(this.genericVariable); } } class Test { static void Main(string[] args) { Generic<int> g = new Generic<int>(5); Generic<string> g1 = new Generic<string>("csharp-console-examples.com"); g.Display(); g1.Display(); } } |
Output:
1 2 3 4 |
5 csharp-console-examples.com |
In the above example, we have defined a generic class – class Generic<T>
. Inside the class, we have treated T as normal data and declared a variable of type T – private T genericVariable;
.
In the constructor also, we are taking a variable of type T – public Generic(T genericValue)
. Remember that the type of T will be decided during making the object of the class.
Generic<int> g = new Generic<int>(5);
→ Here, the type of T is an integer. So, T will become int
inside the definition of the class.
Generic<string> g1 = new Generic<string>("CodesDope");
→ In the object g1, T is string. So, T will become a string inside the definition of the class.
After execution of Generic<int> g = new Generic<int>(5);
, Generic class would be something like:
C# Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Generic { private int genericVariable; public Generic(int genericValue) { this.genericVariable = genericValue; } public void Display() { Console.WriteLine(this.genericVariable); } } |
Let’s take one more example.
C# Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System; class Generic<T, U> { public T GenericVariableFirst { get; set; } public U GenericVariableSecond { get; set; } } class Test { static void Main(string[] args) { Generic<int, string> g = new Generic<int, string>(); g.GenericVariableFirst = 10; g.GenericVariableSecond = "C#"; Console.WriteLine(g.GenericVariableFirst); Console.WriteLine(g.GenericVariableSecond); } } |
Output:
1 2 3 4 |
10 C# |
In this example, we have defined our class to work on two generic types – T and U i.e., class Generic<T, U>
.
While making the object, we have set T as int
and U as string
– Generic<int, string> g
.
C# Constraint
We used a placeholder T in the above examples and this placeholder can be of any type. In C#, we can also constraint the type of placeholder using the where
keyword. Suppose, we have defined a class as:
class ClassName<T> where T: class
In this case, T can only be reference type like class, string, etc. If we try with something else, we will get an error.
Here, class
in where T: class
means T can be a reference type. Let’s look at the table given below for another type of constraints.
Constraint | Description |
---|---|
class | Must be reference type |
struct | Must be value type |
new() | Must have public parameterless constructor. |
BaseClassName | Must be derivied from BaseClassName class. |
InterfaceName | Must implement InterfaceName interface. |
U | Must be or derive from the argument supplied for U. |
Let’s take an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; class Generic<T> where T: class { public T GenericVariable { get; set; } } class Test { static void Main(string[] args) { Generic<int> g = new Generic<int>(); g.GenericVariable = 10; Console.WriteLine(g.GenericVariable); } } |
Output:
hello.cs(16,5): error CS0452: The type ‘int’ must be a reference type in order to use it as type parameter ‘T’ in the generic type or method ‘Generic<T>’
hello.cs(16,26): error CS0452: The type ‘int’ must be a reference type in order to use it as type parameter ‘T’ in the generic type or method ‘Generic<T>’
In this example, we constrained the placeholder to take only reference type using class
and we tried to make an object with an integer for the placeholder. Since int
is a value type, we got errors during compiling the code.
Let’s try with a reference type.
C# Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; class Generic<T> where T: class { public T GenericVariable { get; set; } } class Test { static void Main(string[] args) { Generic<string> g = new Generic<string>(); g.GenericVariable = "csharp-console-examples.com"; Console.WriteLine(g.GenericVariable); } } |
Output:
1 2 3 |
csharp-console-examples.com |
In this example, we tried with a string which is a reference type and thus, the code compiled successfully.
C# Multiple Constraints
We can also have multiple constraints. Let’s take an example.
C# Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System; class Generic<T, U> where T: class where U: struct { public T GenericVariableFirst { get; set; } public U GenericVariableSecond { get; set; } } class Test { static void Main(string[] args) { Generic<string, int> g = new Generic<string, int>(); g.GenericVariableFirst = "csharp-console-examples.com"; g.GenericVariableSecond = 10; Console.WriteLine(g.GenericVariableFirst); Console.WriteLine(g.GenericVariableSecond); } } |
Output:
1 2 3 4 |
csharp-console-examples.com 10 |
Inheritance With Generic Class in C#
We can derive a generic class to make subclasses of it. Let’s take and example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System; class Generic<T> { public T GenericVariable { get; set; } } class Derived: Generic<int> { } class Test { static void Main(string[] args) { Derived d = new Derived(); } } |
In this example, we have made a subclass of a generic class and passed int
during deriving it – class
Derived: Generic<int>
.
We can also make the derived class a generic class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System; class Generic<T> { public T GenericVariable { get; set; } } class Derived<T>: Generic<T> { } class Test { static void Main(string[] args) { Derived<int> d = new Derived<int>(); } } |
C# Generic Methods
We can also have generic methods similar to a generic class. Let’s take an example.
C# Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System; class Test { static void Display<T>(T message) { Console.WriteLine(message); } static void Main(string[] args) { Display("csharp-console-examples.com"); Display(10); } } |
Output:
1 2 3 4 |
csharp-console-examples.com 10 |
CodesDope
10
In this example, we have a generic method Display which has a placeholder T.
Display(“csharp-console-examples.com”);
Display(10);
Firstly, we passed a string and then an integer to the function Display and it worked fine.