With version 2.0 of C#, a new concept was introduced called generics. The basic idea is to allow a specific type like float
, double
, or string
to be passed as a parameter to methods, classes, or even interfaces. There is a crucial limitation to collections, which is the lack or even absence of type checking. However, this allows you to add objects of any type into a collection because they are all descendants of the object
base class. This contradicts the basic definition of C# as type-safe and compromises on type safety.
In this guide, we will take a look at deeper at what generics are and what their open or closed properties mean.
Types in C# have two main categories: value and reference types. Both may be generic types, which take one or more type parameters.
This is one of the most powerful features of C#, helping developers define type-safe datastructures like collections. This not just results in better code quality, but behind the scenes it adds a decent performance boost as well. We'll demonstrate this with a generic Device
class. This approach might be confusing, but for the demonstration's sake we are going to use string
and float
to describe a device. The class will have two properties, name
and category
. Using the float
type can help us calculate costs or operation using numbers the whole time. We won't dig deeper into that part for now.
The code:
1using System;
2
3namespace Pluralsight
4{
5 public class Device<T> {
6 public T name { get; set; }
7 public T category { get; set; }
8 }
9 public class Generics
10 {
11 public static void Main()
12 {
13 Device<string> server = new Device<string>();
14 Device<float> another_server = new Device<float>();
15
16 server.name = "Fabricam-DC1";
17 server.category = "Domain Controller";
18 another_server.name = 1.0f;
19 another_server.category = 1.11f;
20
21 Console.WriteLine($"The server: {server.name} has category: {server.category}");
22 Console.WriteLine($"The server: {another_server.name} has category: {another_server.category}");
23 Console.ReadKey();
24 }
25 }
26}
The code produces the following output:
1The server: Fabricam-DC1 has category: Domain Controller
2The server: 1 has category: 1.11
The instances created have the generic parameter T
passed, which is in the first case string
and the second case float
. After that, when we would like to set the values for specific properties, we are only allowed to use the specific types. This is demonstrated in the assigment of name
and category
properties. This way, type safety is ensured by only accepting the appropriate types of parameters.
If we try to specify something else, the compiler will refuse to execute with the following message:
1CS0029 Cannot implicitly convert type '<type>' to '<type>'
This lets us know that once we are bound to a specific type we are not allowed to assign other type.
Now that we have clarified generics, we need to make a clear distinction between open and closed generics.
The below code demonstrates that you can create an instance of an open type that still contains generic type parameters. Basically, when we say Generic<T>
, or rather, Generic<int>
or Generic<string>
, we create an instance of an abstract class.
1using System;
2using System.Reflection;
3
4namespace Pluralsight
5{
6 public class OG<T>
7 {
8 public enum OGEnum
9 {
10 whohoo = 0
11 }
12 }
13 public class OpenGenerics
14 {
15 public static object WillItRun()
16 => typeof(OG<>.OGEnum).GetTypeInfo().GetDeclaredField("whohoo").GetValue(null);
17 public static void Main()
18 {
19 object foo = WillItRun();
20 Console.WriteLine($"Is this an open generic: {foo.GetType().GetTypeInfo().IsGenericTypeDefinition}");
21 Console.ReadKey();
22 }
23 }
24}
The output is as follows.
1Is this an open generic: True
The Generic<>.GenericEnum
is an open type, because it is nested in an open type.
For the following code, the CLR would complain that it cannot create instances of the open type.
1Enum.GetValues(typeof(Generic<>.GenericEnum))
By using reflection to get a constant field called whohoo
the CLR automatically converts the integer into an instance of the open type.
In this guide, we have learned what open and closed generics are and how to use them in actual applications. Open generics are sort of a strange breed, but with proper caution they can be used to your advantage. The closed ones are what most people tend to use. I hope this guide has been informative to you and I would like to thank you for reading it!