Menu

Leveraging C# Generics

November 22, 2012 - .NET, C#, Programming

Please forgive the ridiculously generic and vague title…

One of the features of modern programming languages is Generics; I won’t bore with the details- most of us are familar with it’s use in generic classes like Dictionary or List. Basically, they are typically considered Data Structure tools; something you would use for some sort of Data structure.

Naturally, of course you can find interesting uses for Generics. Really- you can use them wherever you want to make things more generic. Though, again, that’s a bit of a.. uh, generic description…

Anyway, BASeBlock, one of my C# projects, is a game designed as something of a clone of the older Breakout and arkanoid games. Naturally, the game has Several gameplay parts; obviously we have the Paddle itself, Powerups, and the paddle can get powerups as well. Powerups are dropped by blocks- that particular logic is not important here. The Paddle “Behaviours” are basically power-ups applied to the paddle; things that let you shoot things, attract balls, or that make the paddle sticky, just to name a few.

The way it is written, thewse behaviours are separate; they implement an iPaddleBehaviour interface. The Powerups that actually give these abilities are separate entirely, and are classes derived from the abstract base class, GamePowerUp, which implements all the ‘GamePowerup’ type logic as needed, and calls an abstract method when the paddle collects the powerup, allowing derived classes to concentrate on what they do.

After I added a few Paddle behaviours and their adjoining Powerup to glue them all together, I got to thinking that maybe I could leverage generics in order to make a single class that basically acts to give the paddle a specific behaviour when gathered.

The result actually worked rather well. In code, it looks like this:

Basically, this is a Powerup which takes a Type Parameter that is a iPaddleBehaviour. one might wonder, “Well golly gee, why is it abstract?” This is pretty simple: as it is, the type parameter won’t provide enough information to create a Game Powerup, at least, not reasonable. Each GamePowerup needs to have an image, for example; each powerup would usually use a different image. I could have a base implementation that attempts to use the iPaddleBehaviour’s getIcon, but since we are working with a Type, that means constructing the object just to get that information, and that could have all sorts of freaky side effects. Needless to say I settled for this method, which allows a much shorter class definition for the powerups that just give a behaviour to the paddle. It also allows the addition of things such as making the powerups mutually exclusive with one another as virtual method calls. For example, “StickyPaddlePowerup” which is the Powerup that makes balls stick to the paddle until you click the mouse, has this full source code:

[code]
public class StickyPaddlePowerUp : PaddlePowerUp<PaddleBehaviours.StickyBehaviour>
{
public static float PowerupChance()
{

return 1f;
}
public StickyPaddlePowerUp(PointF Location, SizeF ObjectSize)
: base(Location, ObjectSize)
{
mScoreValue = 40;

}
public override Image[] GetPowerUpImages()
{
return new Image[] { BCBlockGameState.Imageman.getLoadedImage(“SLIMEPOWER”) };
}
}
[/code]

Now, this isn’t super short, but consider that with only minor changes I could even make the Powerup animate as it falls, and there is quite a bit of ability there. There are also a few virtual methods I don’t override for this particular instance.

The result of the above? When it is created (by the blocks when they are destroyed, which between the reflection magic and the weighted probabilities, is a bit beyond the scope of this discussion), it will fall downwards until it either leaves the play area or hits the paddle. when it hits the paddle, it will confer the “StickyBehaviour” Behaviour onto the paddle, and the player is awarded a number of points using the provided score (in this case, it sets the mScoreValue protected member to set that to 40). Here we have this exact class in action:

The Sticky powerup is visible above and slightly to the left of the paddle, it’s the ugly green blob thing. Collect it, and bam- the paddle get’s that ability.

Another interesting consideration (and why I took the approach of not having generic type parameters for anything that would actually be instantiated directly) is pretty simple, the LoadedTypeManager and MultiTypeManager classes, which I believe I discussed previously (but might not have since even I cannot find the post; hard to believe I have 145 of them so far)… well, they don’t really supprot generics. The way I use them is to basically find all derived classes that are Blocks, or Balls, and so forth, and make them available in-game. Generics gum up the works; I believe the class passes the test to see if it derives from or implements the type (if it, of course, actually does), but then the LoadedTypeManager double-checks to make sure it’s “real” by instantiating it. Of course this fails for Generic types.

In order to instantiate a Generic type, you need the “full” type; for example, you can get a Type object like so (using the previous BASeBlock code as a sort of example):

This get’s a valid Type object. In order to do anything particularly useful with it, you need to use a Method of that Type- MakeGenericType, and pass in an array of Types corresponding to the Generic Type Parameters of the class; it returns another Type Object that you can Instantiate, because it’s a “full” type, of sorts.

This presents a problem for me, since the LoadedTypeManager sort of needs to be able to instantiate them. I toyed with the idea of allowing only bounded generics; such as the above, which only accepts type parameters that are a given type- but decided I was really compounding things; I would have to cache all the Generic types I found in separate assemblies and then afterwards go back and look for classes that satisfy the constraints of all the generic classes that I found along the way. Right now I just let them pass into the core checking routine; the attempt to construct the object throws an Exception, and life continues.

I’m starting to think I might not have written a post about the LoadedTypeManager class (or MultiTypeManager, it’s big brother that uses it as a composite), which is actually a good thing since it will give me something to write about. Even if I did, I did make several changes in the meantime too.

Have something to say about this post? Comment!