Adam's blog formerly game development, now just casual madness

C# 4.0 powers, ACTIVATE!

When I began to write ZweiDe back in 2007, the target framework was .Net 2.0. The third framework version was already released, but I assumed it would severely impact compatibility to require the newest stuff available. As it took years to bring ZweiDe anywhere near release, it was of course irrelevant. I didn’t care much since I had no practical idea of what I was missing anyway. In Duality, calculating with an estimated development time of forever, I figured out it might be worth a try targeting .Net 4.0 right from start.

I read some wiki entries to get a quick glance at the new language features and classes. Then some tutorials. It all seemed nice, yet not quite necessary – but after a while, I could hardly go back without really really missing something. If I had to pick a single, favorite feature, I would chose Extension Methods. As their name suggests, they allow you to extend existing classes with your own methods. Simple case: Assume you need a random color. Here’s what you’d probably do without Extension Methods:

public static class MathHelper
{
	public static ColorRGBA GetRandomColorRGBA(Random r)
	{
		return new ColorRGBA(
			(byte)(r.Next() % 256),
			(byte)(r.Next() % 256),
			(byte)(r.Next() % 256),
			255);
	}
}

// Need a random color
ColorRGBA clr = MathHelper.GetRandomColorRGBA(myRnd);

Using Extension Methods, the code looks like this:

public static class ExtMethodsRandom
{
	public static byte NextByte(this Random r)
	{
		return (byte)(r.Next() % 256);
	}
	public static ColorRGBA NextColorRGBA(this Random r)
	{
		return new ColorRGBA(
			r.NextByte(),
			r.NextByte(),
			r.NextByte(),
			255);
	}
}

// Need a random color
ColorRGBA clr = myRnd.NextColorRGBA();

Pretty much some kind of syntactical sugar. But one that feels extremely elegant; instead of accessing some seemingly unrelated class, you write your class instances variable name, hit the dot button, the auto completion menu pops up and there they are: All those helper methods extending that specific class.

However, let’s take it a little further: Extension Methods can also be defined generic and extending interfaces. C# 4.0 comes with IEnumerable extensions providing a lot of useful set operations (which by the way would be my second favorite feature). In context of Dualitys component-based GameObject system, I added some custom ones:

public static class ExtMethodsIEnumerable
{
	// Want to iterate through all the children of a group of objects? Here you go:
	public static IEnumerable<GameObject> Children(this IEnumerable<GameObject> objEnum)
	{
		foreach (GameObject o in objEnum)
			foreach (GameObject c in o.Children) yield return c;
	}

	// Not enough? You need specific components of a group of objects? Ka-ching!
	public static IEnumerable<T> GetComponents(this IEnumerable<GameObject> objEnum, bool activeOnly = false) where T : Component
	{
		foreach (GameObject o in objEnum)
			foreach (T c in o.GetComponents<T>(activeOnly)) yield return c;
	}

	// Same thing, the other way around
	public static IEnumerable<GameObject> GameObject(this IEnumerable<Component> compEnum, bool activeOnly = false)
	{
		foreach (Component c in compEnum)
			if (c.GameObj != null && (!activeOnly || c.GameObj.Active)) yield return c.GameObj;
	}

	// etc.
}

Those Extension Methods simplify things a lot. Whenever there is an enumeration of Components, it’s just a single word to query all of their GameObjects.

// Need some Components
Component[] someComponentArray = someClassInstance.GetMeSomeComponents();
// Oh snap! Now I want to access all their active GameObjects!
foreach (Component c in someComponentArray)
{
	GameObject obj = c.GameObj;
	if (obj == null || !obj.Active) continue;
	// ...
}
// Same thing shorter:
foreach (GameObject obj in someComponentArray.GameObject(true))
{
	// ...
}

Here’s some actual usage example from the Duality Editor:

// Goal: Find out which GameObjects are currently selected indirectly.
// That is: They are not selected, but their parent GameObject is,
// or alternatively, one of their own Components.

// Retrieve GameObjects selected via Components - but exclude GameObjects that are directly selected.
var indirectViaCmpQuery = selectedComponents.GameObject().Except(selectedGameObjects);
// Retrieve GameObjects selected because their parent is directly selected.
var indirectViaChildQuery = selectedGameObjects.ChildrenDeep();
// Concatenate both queries, but assure no GameObject is enumerated twice.
var indirectQuery = indirectViaCmpQuery.Concat(indirectViaChildQuery).Distinct();

// BAM!

Yeah, I know. Probably not super impressive code - but still very useful!

55 of 74