<< Back

Generics

This lesson is going to build up some components we'll use later. At the end of this one, we won't have added any new behavior to the program, but we'll be in a good place to do so. To start with, we'll create a new class named Weighted.

This looks mostly like any other class, except now we have this strange <T> thing. What's that about? This is called a generic type parameter which makes this class a generic class. The name of this class would be read as "Weighted of T" and what it does is let you attach some other type to your class. Why would you do that? Let's do a little more first.

The class is done.

Adding record before class does several things: it makes it so that the class automatically gets some comparison code included that we may end up discussing later, and lets you define the unchanging properties and constructor just by adding a parameter list to the class name. We have defined a property of type T named Item and an integer property named Weight.

Let's talk about a quick example of how you'd make a variable of this type: var thing = new Weighted<string>("Hello", 40); This defines an object named "thing" that contains the string "Hello" and a weight of 40. You can make Weighted objects of whatever type you want.

So next, we'll talk about how you can use this class for something useful. But for that, we'll need to make another unusual type of class.

This class is called a static class - static classes are classes that you can't make an object from, but can use methods from. You have already used one previously: the Console class. They also have the ability to declare special types of methods that extend the behavior of other classes elsewhere. We're going to use it to get a random value from a list of weighted values.

Here we create a field - the kind of class member variable suitable for use only within the class itself. It has been declared as private to keep it from being accessed from outside, and static so there can only be one single random number generator instance. The lack of curly braces and accessor list is what marks this as a field rather than a property, and makes it only able to hold a value, and not run calculations when accessed. The Random class is a commonly used C# type used to generate random numbers in various ways.

This is the weirdest looking method yet. First, it's static, much like the WriteLine method you've been using from the Console class. Second, it returns T, and has a generic T in a couple of places. That seems confusing, but the thing that really matters is the parameter list. You only have one, and it's marked with this. The parameter is an IEnumerable<Weighted<T>>. IEnumerable is a thing called an interface, which defines a sort of category of object which in this case includes arrays, lists, and many more types of groups of things that can be counted. Fundamentally, this method can be passed any kind of list of weighted anythings.

If you were to make an array of weighted strings, you might do it like: var x = new[] { new Weighted<string>("Hello", 40) }; This is an array containing only a single weighted string. Because you defined the GetRandomWeightedValue method's parameter as this, you can now call it as var y = x.GetRandomWeightedValue(); - the x actually gets passed into the method, but looks like you're using a method on the array! You shouldn't do this too often, but sometimes, like now, it is useful to do so.

Okay, a lot of unfamiliar stuff again this time, but I promise it's not that bad. First, we're making a variable to hold a new list of weighted values. In this list, the weight of each new value will be the sum of itself and all previous values. So if you have three items with weights 30, 40, and 50, their weights will now be 30, 70, and 120. We do this by using the foreach loop, which goes through all of the input weighted values one by one and creates a copy of them with the same Item but a new weight based on the last weight in the list (or 0 if there are none). We then ask the random number generator for the next random number, and we're passing in the maximum value we want it to be allowed to generate, which is the last element's weight, or if there are none, 0. We then return the Item property of the first weighted item with a higher weight than the random number. The argument passed into the function to do this seems rather strange perhaps. It is called a lambda function, or a function (method) with no name. It takes a parameter of a weighted item which we call "w", and then checks if it is higher than the random number, and if so returns true, or else returns false. The First method loops through and finds the first time that becomes true, and returns it.

So, given a list or array or the like of items with weights, you can now get a random one, respecting the relative weightings.