Using Generics

Using Generics

The Crash Course

  • Generics in C# are a similar concept to generics in Java and templates in C++. They are a way to create type safe classes without actually needing to commit to any specific data type.
  • When you create an instance of a class that uses generics, you'll need to indicate which type (or types) you are using it for in any particular instance using the angle brackets ('<' and '>'): List<string> listOfStrings = new List<string>();
  • This tutorial also covers some of the details of using the List class and the Dictionary class, both of which use generics. The Dictionary class can be thought of as a hash table, or lookup table.

Introduction

In this tutorial, we'll take a look at a powerful feature in C# called generics. If you're coming from a Java background, this is the same kind of generics that Java has, and if you're coming from a C++ background, this is more or less the same concept behind templates. If you're coming from a different background, or no background at all, have no need to fear; this tutorial will explain everything!

We'll start by taking a look at why generics even exist in the first place. The problem they solve. We'll then look at two different classes that come with the .NET framework that use generics. These classes are the List class and the Dictionary class. These classes (especially the List class) have a very wide variety of uses, and as we make software, we'll definitely be putting them and other generic classes to good use.

In the next tutorial, we'll look at how to actually create your own class that uses generics.

The Motivation for Generics

Before we can really discuss generics, we need to discuss why they exist in the first place. So I'm going to start by trying to get you to think about the underlying problem that generics will address.

We've been doing stuff with classes for a little while now. Let's say you wanted to make some sort of class to store a list of numbers. (Yeah, OK, we've already got arrays, but maybe this list of numbers is special. Like maybe it is sorted or something.) I know I just said there's a built-in List class, and there is, but this is just an example, so go with it for now.

Imagine, for a second, what you'd need to do to make a class to accomplish this. You might do something like this:

public class ListOfNumbers
{
    private int[] numbers;
 
    public ListOfNumbers()
    {
        numbers = new int[0];
    }
 
    public AddNumber(int newNumber)
    {
        // Add some code in here to make a new array, slightly larger
        // than it already was, and add your number in.
    }
 
    public object GetNumber(int index)
    {
        return numbers[index];
    }
}

It's not complete, but you get the idea. You make your class, using the int type all over the place.

But now let's say you want a list of strings. Isn't it a shame that you can't use your ListOfNumbers class, and put strings in it? So what now? Any ideas?

I suppose we could make a very similar class called ListOfStrings, right? It would end up looking basically the exact same as our ListOfNumbers class, only with the string type instead of the int type. In fact, we could go crazy making all sorts of ListOf… classes, one for every type imaginable. But that's kind of annoying, because we could have almost a limitless number of those classes. Lists of ints, lists of strings, lists of lists….

Maybe there's another way. What if we simply made a list of objects? Remember how object is the base class of any type of object?

We could create just a simple List class that uses the object type:

public class List
{
    private object[] objects;
 
    public List()
    {
        objects = new object[0];
    }
 
    public void AddObject(object newObject)
    {
        // Add some code in here to make a new array, slightly larger
        // than it already was, and add your object in.
    }
 
    public object GetObject(int index)
    {
        return objects[index];
    }
}

Now we can put any type of object we want in it. We only need to create one class (just the List class) for any type of object we want.

This might seem to work at first glance, but it turns out, it's got a few problems as well. For instance, any time you want to do anything with it, you'll need to cast stuff to the type you're putting in it.

If it is supposed to be a list of strings, so we're putting /strings in it, and we want to call the GetObject method, we'll need to something like this:

string text3 = (string)list.GetObject(3);

Casting takes time to execute, so it slows things down a bit, and it also is a bit annoying, because you'll have to keep doing it throughout your code.

But there's a second, bigger problem here. Let's assume we're sticking strings in our list. We can cast all we want, like in the example just a second ago, but since the list is a list of objects, we could put anything in the list, not just strings.

I mean, we could be extremely careful, within our program, and make sure that we don't, but there's nothing preventing us from accidentally putting something else in there, like an instance of the Random class. (After all, there's nothing wrong with saying list.AddObject(new Random());, even when we're supposed to just be putting strings in it. And even if we are careful, someone else on the team, or someone else using the code on the other side of the world may not be as careful.

So basically, we never can know, for sure, what type of object we're pulling out. We'll always have to check and make sure that it is the type we think it is, because it might possibly be something else. Programmers have a name for this, by the way. They say it isn't "type safe". Type safety is where you always know what type of object you are working with.

Type safety isn't a problem that our first approach had, because we had a list of strings, or whatever type we were using. We knew exactly what we were working with, and we were able to ensure that we were using the right thing all the time, and there was no need for casting.

So we've got two bad choices here. One, we make "type safe" classes of lists, one for every type we want to use, and call them ListOfInts, ListOfStrings, and so on, or two, make one single, type unsafe class of plain old objects, that isn't type safe, but doesn't require making lots of different versions.

But you're here reading this tutorial, so you know there's got to be a better way to deal with this. And there is. Generics!

What are Generics?

Generics is a clever way for you to make "generic" classes when you create the class ("create" as in, write the code to define the class, not actually creating an instance of the class), and then when you want to use them, you simply state which type you want to use at the time. So one time, you'll use the generic class and say "this time it is a list of ints", and another time, you'll say "now I want to make a list of Player objects".

In short, generics provide a way to create type safe classes, without having to actually commit to any particular type when you create the class.

Perhaps one of the best ways to really understand how generics work is to actually see a couple of classes that uses generics. So we'll do that now. We'll start with the List class, which is pretty close to the stuff I've been talking about in this section, and then we'll look at the Dictionary class, which is a bit more advanced, and shows off a few more of the features that generics has to offer. In the next tutorial, we'll see how to add generics to our own classes.

The List Class

Lists are so commonly used that, as you can probably guess, there's already a List class that has been made for you. Let's take a look at that class, and explore how lists work a bit further.

So the class is called List, so you might think you can create a list like this:

List listOfStrings = new List(); // This doesn't quite work...

But you can't, because the it uses generics! To create an instance of the List class, you will also need to specify the type of stuff you're putting into the list. To do this, you put the type you want to use inside of the angle brackets ('<' and '>'). So if you want a list of strings, you would do this:

List<string> listOfStrings = new List<string>();

You now have a list containing strings! But wait, it gets better. There's an Add method that you can use to add items to the back of the list:

listOfStrings.Add("Hello World!");

If you look closely, though, you'll notice that since the type we are using is List<string>, this method requires that we put only strings in. The compiler knows that you can't put anything else in it. So it is type safe. But we've only had to define a single "generic" list class. We've gotten the best of both sides.

That really covers the fundamentals of generics. But let's spend a little more time looking at this List class, since it is so useful.

You can add new items to the list in a couple of ways. First, there's the Add method, which we just saw:

List<string> strings = new List<string>();
strings.Add("text1");
strings.Add("text2");

The Add method puts the new items at the back end of the list.

There's also the Insert method, which allows you to put in the index to add the item at, pushing everything else back:

strings.Insert(0, "text3");

Remember that indexes typically start at 0, so this is adding at the very front of the list.

You can also get an item out of the list using the ElementAt method.

listOfStrings.ElementAt(0);

Note, though, that this is an extension method, which is something we'll talk about in a future tutorial.

There's another way to get and set items in the list. You can use the square brackets ('[' and ']') with the List class, just like with an array. (They've defined an indexer for the List class, which is also something we'll talk about in a future tutorial.)

So you can say:

string secondItem = listOfStrings[1];

And you can also say:

listOfStrings[0] = "This message replaces whatever was already at index 0";

Also, the RemoveAt method allows you to delete items from the List:

listOfStrings.RemoveAt(2);

While we're talking about deleting stuff, you can delete everything from the List with the Clear method:

listOfStrings.Clear();

While arrays have the Length property to determine how many items are in the array, with the List class, you'll use the Count property instead (there's no Length property).

int itemsInList = listOfStrings.Count;

Now if we're using a List, but we want to change it over to an array, there's an easy way to do this. There's a ToArray method that will make this conversion for us, turning our generic class into an array of the appropriate type:

List<int> someNumbersInAList = new List<int>();
someNumbersInAList.Add(14);
someNumbersInAList.Add(24);
someNumbersInAList.Add(37);
 
int[] numbersInArray = someNumbersInAList.ToArray();

One other final thing worth looking at with the List class is the ability to loop over items inside it, just like with arrays:

List<int> someNumbersInAList = new List<int>();
someNumbersInAList.Add(14);
someNumbersInAList.Add(24);
someNumbersInAList.Add(37);
 
foreach(int number in someNumbersInAList)
{
    // ...
}

Remember that since the List class is generic, you can create a List class of any type or class that you want. And when you do, all of the methods like Add and ElementAt will work only for the type that you are using. That was the point of generics to begin with. (Note, though, that derived classes can still go in a list of the base type.)

Using the Dictionary Class

To wrap up our introduction on using generics, we're going to look at one final class that uses them in a way that is slightly more complex than the List class. We'll take a look at the Dictionary class. This class isn't as versatile as the List class, but it definitely has its uses, and so it is a good choice to look at.

Let's start by describing what the Dictionary class actually is. If you are coming from a programming background, then I can probably summarize this for you by saying that it is a hash table, or a lookup table. For those of you who don't know what either of those are, I'll explain.

Actually, the name of the class is a pretty good description of what this is. This class works like a dictionary does. (You remember books, right?) In a dictionary, you have a large list of words and a single definition that belongs to each of them. When you use a dictionary, you (hopefully) efficiently search through the dictionary, finding the word that you are interested in, to get the definition of the word. Basically, you use one piece of text to look up another piece of text.

The Dictionary class does a very similar thing. We use one piece of information (what's called the "key") to store and look up another piece of information (what's called the "value"). (So it is a mapping of key/value pairs.) Only, because the class is generic, we're not stuck to using words (strings). We can use any type that we want.

For example, let's use a Dictionary to create a phone book. We'll use strings for people's names, and ints for their phone numbers. (OK, perhaps ints aren't the best choice, but I want a simple example that uses two different types. Yes, we could easily create a Person or Contact class that stores names and other personal information, and a PhoneNumber class that deals with all of the details of phone numbers, like country codes and area codes, and so on. But we're going to keep it simple.)

The Dictionary class is generic, just like the List class, but it has two different types that are generic—the key, which we use to do the lookup, and the value that belongs to it.

Instead of just putting one type in the angle brackets, we'll put two in, one for each of the pieces that are generic:

Dictionary<string, int> phoneBook = new Dictionary<string, int>();

We can now use the indexing operator ('[' and ']') to get or set values in the Dictionary:

phoneBook["Gates, Bill"] = 5550100;
phoneBook["Zuckerberg, Mark"] = 5551438;
 
int billsNumber = phoneBook["Gates, Bill"]; // We're good buddies...

(For the record, this isn't actually Bill Gates' phone number.)

Like with the List class, there are a lot of methods that you can use with the Dictionary class, so feel free to explore it and see what you can do with it.

What's Next?

Generics are very powerful, and allow us to make "container" classes, or "collection" classes that can be used for any type of object. We've looked at both the List class and the Dictionary class, both of which will prove to be very useful as we make software.

Now that we know how generics work, in terms of using them, let's go ahead and see how we could actually create generics in our own classes.