C# Generics
Generics allow us to treat different types and classes similarly through the power of type parameters. For example, rather than defining the same method across multiple classes, we can define a single class that can take in multiple types in a single location. Hopefully the examples provided below can help illuminate exactly what I'm talking about.
Let's say we have a backpack that we put a bunch of stuff into. Books, pencils and notebooks, for example. We might make a class Backpack like this:
public class Books { private Collection<Book> BookCollection; public void Add(Book book) { this.BookCollection.Add(book); } } public class Pencils { private Collection<Pencil> PencilCollection; public void Add(Pencil pencil) { this.PencilCollection.Add(pencil); } }
Can you see something wrong here? We have 3 methods with the same exact name, and they are all adding something of some type to that type's collection. This is a serious violation of DRY. Why do we have to do this though? Because C# requires us to define what we are performing the action on - it has not idea what's going on otherwise. So how can we reduce our code footprint while simultaneously giving C# something to define? Generics.
Rather than having to create a separate, unique list for each one of our classes - we can simply make a GenericList class that creates a new list upon instantiation. We defer type specification until instantation without the cost or risk of runtime casts or boxing operations.
public class GenericList<T> : IEnumerable { private Collection<T> GenericCollection; public IEnumerator<T> GetEnumerator() { return this.GenericCollection.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } }
public class TestClass { static void Main() { var bookCollection = new GenericList<Books>(); var pencilCollection = new GenericList<Pencils>(); bookCollection.GetEnumerator(); pencilCollection.GetEnumerator(); } }
Once we have that, we can use a GenericList where ever and whenever - this gives us more flexibility and reduces the amount of code. Generics can be seen as an instance of composition, whereby we extract some common behavior or property from classes. This is especially useful for collections of different data types, such as Dictionaries. Typically, we use generics more often than creating them - regardless, generics are a great way to reuse code and improve performance, specifically by avoiding boxing/unboxing and casting operations.