Continuing from last time, this article begins covering features of C# classes that aren’t in AS3. We’ll begin with abstract classes and functions, which AS3 required workaround code to enforce even at run-time. Today’s article shows you how to use C# to cleanly enforce these at compile-time.

Table of Contents

First off, I forgot one feature of AS3’s classes in the previous three articles. Let’s briefly discuss static static initializers, also known as class initializers, class constructors, or static constructors. This is a function that is called automatically when the class’ static fields need to be initialized prior to the class being used. Here’s how it looks in AS3:

class Person
{
    private static var NEXT_ID:int;
    private var id:int;
 
    // static initializer:
    {
        NEXT_ID = 1;
    }
 
    // instance constructor
    function Person()
    {
        id = NEXT_ID++;
    }
}

Static initializers aren’t commonly used because you can declare and initialize your fields at the same time (private static var NEXT_ID:int = 1;), but can be useful if the initialization requires more complex logic. In any case, here’s the equivalent in C#:

class Person
{
    private static int NextID;
    private int id;
 
    // static initializer:
    static Person()
    {
        NextID = 1;
    }
 
    // instance constructor
    Person()
    {
        id = NextID++;
    }
}

Static initializers in C# are called a “static constructor”. It’s like a constructor for the class rather than a constructor for an instance of the class. It’s syntax is just like that of an instance constructor, except that it has the static keyword first, it never has an access modifier, and it doesn’t take any parameters.

Now let’s talk about abstract classes. These are classes that can’t be instantiated directly. To create one, you need to extend the abstract class with a non-abstract class and instantiate the extending class. AS3 lacks this feature at compile-time, but there is a common run-time workaround:

class ExtrudedShape
{
    private var depth:int;
 
    protected static const HIDDEN_KEY:Object = {};
 
    function ExtrudedShape(ABSTRACT:Object, depth:int)
    {
        if (ABSTRACT != HIDDEN_KEY)
        {
            throw new ArgumentError("ExtrudedShape is an abstract class");
        }
 
        this.depth = depth;
    }
 
    function get area(): int
    {
        return 0;
    }
 
    function get volume(): int
    {
        return depth * area;
    }
}

With this workaround you can still write code that creates an ExtrudedShape directly and it will compile just fine:

var shape:ExtrudedShape = new ExtrudedShape(null, 3);

At runtime, the first argument will be checked against the private const Object that anyone outside of the class and its derivatives couldn’t possibly have access to. The ArgumentError will then be thrown and the caller will not get an instance of the ExtrudedShape. A derivative class, on the other hand, has access to HIDDEN_KEY and can pass it to the constructor:

class ExtrudedCircle extends ExtrudedShape
{
    function ExtrudedCircle(depth:int)
    {
        super(HIDDEN_KEY, depth);
    }
}

This is an effective strategy for implementing abstract classes at run-time, but C# offers a way to enforce this at compile-time:

abstract class ExtrudedShape
{
    private int depth { get; private set; }
 
    ExtrudedShape(int depth)
    {
        this.depth = depth;
    }
 
    int Area
    {
        get { return 0; }
    }
 
    int Volume
    {
        get { return depth * Area; }
    }
}

Note the use of the abstract keyword when declaring the class. This tells the compiler not to let anyone directly instantiate the class. There’s no need for any of the runtime workaround that AS3 requires. Derivative classes don’t need any HIDDEN_KEY and look just like classes extending any other non-abstract classes:

class ExtrudedCircle : ExtrudedShape
{
    ExtrudedCircle(int depth)
        : base(depth)
    {
    }
}

Similar to abstract classes are abstract functions. These are used when you want your abstract class to not define a function but instead mandate that classes extending it must implement the function. There is, again, no way to do this in AS3 at compile-time. Instead, it can be worked around at runtime:

class ExtrudedShape
{
    private var depth:int;
 
    protected static const HIDDEN_KEY:Object = {};
 
    function ExtrudedShape(ABSTRACT:Object, depth:int)
    {
        if (ABSTRACT != HIDDEN_KEY)
        {
            throw new ArgumentError("ExtrudedShape is an abstract class");
        }
 
        this.depth = depth;
    }
 
    function get area(): int
    {
        throw new Error("'get area' is an abstract function");
        return 0;
    }
 
    function get volume(): int
    {
        return depth * area;
    }
}

Here the ExtrudedShape class doesn’t want to define the get area function since it really doesn’t know anything about that. So its version of get area immediately throws an exception. This makes the function effectively abstract at run-time, but there is again no compile-time checking. The following derivative class compiles just fine without implementing the get area function:

class ExtrudedCircle extends ExtrudedShape
{
}

In C#, we simply use the abstract keyword:

abstract class ExtrudedShape
{
    private int depth { get; private set; }
 
    ExtrudedShape(int depth)
    {
        this.depth = depth;
    }
 
    abstract public int Area
    {
        get;
    }
 
    int Volume
    {
        get { return depth * Area; }
    }
}
class ExtrudedCircle : ExtrudedShape
{
    private int area;
 
    override int Area
    {
        get { return area; }
    }
}

The same keyword would be used for non-getter functions, too:

abstract class GameEntity
{
    abstract void TakeDamage(int damage);
}
 
class Enemy : GameEntity
{
    int health;
 
    override void TakeDamage(int damage)
    {
        health -= damage;
    }
}

That wraps up abstract classes and functions for today, as well as static initializers. To summarize, here’s a comparison between C# and AS3 covering everything in this article:

////////
// C# //
////////
 
// Abstract class
abstract class GameEntity
{
	private static int NextID;
 
 
 
	protected int health;
 
	int id;
 
	static GameEntity()
	{
		NextID = 1;
	}
 
	GameEntity(int health)
	{
 
 
 
 
 
		this.health = health;
		this.id = NextID++;
	}
 
	// Abstract property
	bool Friendly
	{
 
		abstract get;
	}
 
	// Abstract function
	abstract void TakeDamage(int amount)
	{
 
	}
}
 
// Non-abstract ("concrete") class
class Enemy : GameEntity
{
	Enemy(int health)
		: base(health)
	{
	}
 
	// Implemented abstract property
	override bool Friendly
	{
		get { return false; }
	}
 
	// Implemented abstract function
	override void TakeDamage(int amount)
	{
		health -= amount;
	}
}
/////////
// AS3 //
/////////
 
// Abstract class - only enforced at run-time
class GameEntity
{
	private static var NEXT_ID:int;
 
	protected static const HIDDEN_KEY:Object = {};
 
	protected var health:int;
 
	var id:int;
 
	// Static initializer
	{
		NEXT_ID = 1;
	}
 
	function GameEntity(ABSTRACT:Object, health:int)
	{
		if (ABSTRACT != HIDDEN_KEY)
		{
			throw new ArgumentError("GameEntity is abstract");
		}
 
		this.health = health;
		this.id = NEXT_ID++;
	}
 
	// Abstract property/getter - only enforced at run-time
	function get friendly(): Boolean
	{
		throw new Error("'get friendly' is abstract");
		return false;
	}
 
	// Abstract function - only enforced at run-time
	function takeDamage(amount:int): void
	{
		throw new Error("takeDamage is abstract");
	}
}
 
// Non-abstract ("concrete") class
class Enemy extends GameEntity
{
	function Enemy(health:int)
	{
		super(HIDDEN_KEY, health);
	}
 
	// Implemented abstract property
	override function get friendly(): Boolean
	{
		return false;
	}
 
	// Implemented abstract function
	override function takeDamage(amount:int): void
	{
		health -= amount;
	}
}

Next time we’ll continue with C# class features that aren’t available in AS3. Stay tuned!

Continue to Part 5

Spot a bug? Have a question or suggestion? Post a comment!