Let’s continue the From AS3 to C# series from last time by continuing to investigate C# classes from an AS3 developer’s point of view. Today’s article will cover class inheritance, interface implementing, and interface inheritance.

Table of Contents

First up today is class inheritance. Here’s how it looks in AS3:

class Shape
{
}
 
class Circle extends Shape
{
}

Now here’s how it looks in C#:

class Shape
{
}
 
class Circle : Shape
{
}

As you can see, a simple : is used in place of the extends keyword.

Similar to inheritance, here’s how you’d say that your class has implemented some interfaces in AS3:

interface IHasArea
{
    function GetArea(): int;
}
 
interface IRound
{
    function GetSmoothness(): int;
}
 
class Shape
{
}
 
class Circle extends Shape implements IHasArea, IRound
{
    function GetArea(): int
    {
        return 33; // TODO calculate
    }
 
    function GetSmoothness(): int
    {
        return 44; // TODO calculate
    }
}

And here’s the same structure in C#:

interface IHasArea
{
    int GetArea();
}
 
interface IRound
{
    int GetSmoothness();
}
 
class Shape
{
}
 
class Circle : Shape, IHasArea, IRound
{
    int GetArea()
    {
        return 33; // TODO calculate
    }
 
    int GetSmoothness()
    {
        return 44; // TODO calculate
    }
}

In C#, we see that : is used in place of the implements keyword just as it was with extends. Interfaces are also shown here and they’re declared just like as in AS3. Here’s how interfaces extend each other in AS3:

interface ISuper
{
}
 
interface ISub extends ISuper
{
}

You can probably guess how the C# version will look…

interface ISuper
{
}
 
interface ISub : ISuper
{
}

Now that we’ve seen how to declare these parent-child relationships in classes and interfaces, let’s see how our code can reference them. In AS3, you can use this and super in any non-static function of your class to refer to the class instance the function is operating on and the parent class instance the function is operating on, respectively. Here’s an example:

class Polygon
{
    var numVertices:uint;
 
    function printDescription(): String
    {
        return "Polygon (numVertices=" + numVertices + ")";
    }
}
 
class Triangle extends Polygon
{
    var color:String;
    var polygonDescriptionAtConstructionTime:String;
 
    function Triangle(color:String)
    {
        this.color = color;
        polygonDescriptionAtConstructionTime = super.printDescription();
    }
 
    function printDescription(): String
    {
        return "Triangle (color=" + color + ")";
    }
}

The super keyword is explicitly referring to the Polygon class’ variables and functions. It’s rare to use this, but helps clear up the ambiguity between which printDescription function to call. Without this, the Triangle class’ printDescription would be called as the calling class is searched before its parent class.

The this keyword is explicitly referring to the Triangle class’ variables and functions. It’s used in the constructor to clarify that the color variable to assign to is the version held by the instance of the Triangle class that the constructor is working on, not the parameter with the same name that was passed to the constructor.

Now let’s see the C# version:

class Polygon
{
    uint NumVertices;
 
    String PrintDescription()
    {
        return "Polygon (numVertices=" + numVertices + ")";
    }
}
 
class Triangle : Polygon
{
    String Color;
    String PolygonDescriptionAtConstructionTime;
 
    Triangle(String color)
    {
        this.Color = color;
        PolygonDescriptionAtConstructionTime = base.printDescription();
    }
 
    String printDescription()
    {
        return "Triangle (color=" + color + ")";
    }
}

These two snippets of code are very similar, except that the C# version uses base instead of super. The “base” class in C# is the same as the “super” class in AS3.

Now for the other use of super in AS3, to call the parent class’ constructor:

class Polygon
{
    var numVertices:uint;
 
    function Polygon(numVertices:uint)
    {
        this.numVertices = numVertices;
    }
}
 
class Triangle extends Polygon
{
    var color:String;
 
    function Triangle(numVertices:uint, color:String)
    {
        super(numVertices);
        this.color = color;
    }
}

Here’s the C# version:

class Polygon
{
    uint NumVertices;
 
    Polygon(uint numVertices)
    {
        NumVertices = numVertices;
    }
}
 
class Triangle : Polygon
{
    String Color;
 
    Triangle(uint numVertices, String color)
        : base(numVertices)
    {
        Color = color;
    }
}

Other than swapping out super for base again, we can see that the call is now happening before the constructor’s function body: before the curly braces ({}). It’s also preceded by a colon (:), similar to how the class extends another class or implements an interfaces. Also, there’s no need to add a semicolon at the end of this line.

Now let’s see the AS3 way of making sure that a class can’t be extended:

final class FinalClass
{
}
 
// the following class won't compile...
class DerviedClass extends FinalClass
{
}

C# has a similar feature:

sealed class FinalClass
{
}
 
// the following class won't compile...
class DerviedClass : FinalClass
{
}

Just like “super” and “base”, this is just a terminology difference. C# calls a class that can’t be extended a “sealed” class where AS3 refers to it as a “final” class. The effect is the same, though.

Lastly, here’s a comparison between C# and AS3 covering everything in this article:

////////
// C# //
////////
 
// Interface
interface IShape
{
    String GetDescription();
}
 
// Sub-interface (one that extends another)
interface IPolygon : IShape
{
    uint GetNumEdges();
}
 
// Class
class Shape
{
    String Name;
 
    Shape(String name)
    {
        Name = name;
    }
}
 
// Derived class (one that extends another)
class Circle : Shape
{
    int Radius;
 
    Circle(String name, int radius)
        : base(name)
    {
        Radius = radius;
    }
}
 
// Derived class that also implements an interface
class Triangle : Shape, IPolygon
{
    Triangle()
        : base("Triangle")
    {
    }
 
    uint GetNumEdges()
    {
        return 3;
    }
 
    String GetDescription()
    {
        return "A three-sided polygon";
    }
}
 
// Class that can't be extended
sealed class NoDerivatives
{
}
/////////
// AS3 //
/////////
 
// Interface
interface IShape
{
    function getDescription(): String;
}
 
// Sub-interface (one that extends another)
interface IPolygon extends IShape
{
    function getNumEdges(): uint;
}
 
// Class
class Shape
{
    var name:String;
 
    function Shape(name:String)
    {
        this.name = name;
    }
}
 
// Derived class (one that extends another)
class Circle extends Shape
{
    var Radius:int;
 
    function Circle(name:String, radius:int)
    {
        super(name);
        Radius = radius;
    }
}
 
// Derived class that also implements an interface
class Triangle extends Shape implements IPolygon
{
    function Triangle()
    {
        super("Triangle");
    }
 
    function GetNumEdges(): uint
    {
        return 3;
    }
 
    function GetDescription(): String
    {
        return "A three-sided polygon";
    }
}
 
// Class that can't be extended
final class NoDerivatives
{
}

We’ll leave off here for now. Next time we’ll cover getters and setters and finish off the basics of classes before moving on to advanced concepts that go beyond what AS3 classes could do.

Continue to Part 3

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