From AS3 to C#, Part 2: Extending Classes and Implementing Interfaces
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
- From AS3 to C#, Part 1: Class Basics
- From AS3 to C#, Part 2: Extending Classes and Implementing Interfaces
- From AS3 to C#, Part 3: AS3 Class Parity
- From AS3 to C#, Part 4: Abstract Classes and Functions
- From AS3 to C#, Part 5: Static Classes, Destructors, and Constructor Tricks
- From AS3 to C#, Part 6: Extension Methods and Virtual Functions
- From AS3 to C#, Part 7: Special Functions
- From AS3 to C#, Part 8: More Special Functions
- From AS3 to C#, Part 9: Even More Special Functions
- From AS3 to C#, Part 10: Alternatives to Classes
- From AS3 to C#, Part 11: Generic Classes, Interfaces, Methods, and Delegates
- From AS3 to C#, Part 12: Generics Wrapup and Annotations
- From AS3 to C#, Part 13: Where Everything Goes
- From AS3 to C#, Part 14: Built-in Types and Variables
- From AS3 to C#, Part 15: Loops, Casts, and Operators
- From AS3 to C#, Part 16: Lambdas and Delegates
- From AS3 to C#, Part 17: Conditionals, Exceptions, and Iterators
- From AS3 to C#, Part 18: Resource Allocation and Cleanup
- From AS3 to C#, Part 19: SQL-Style Queries With LINQ
- From AS3 to C#, Part 20: Preprocessor Directives
- From AS3 to C#, Part 21: Unsafe Code
- From AS3 to C#, Part 22: Multi-Threading and Miscellany
- From AS3 to C#, Part 23: Conclusion
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.
Spot a bug? Have a question or suggestion? Post a comment!
#1 by henke37 on July 28th, 2014 ·
This obviously means that C# constructors must always call the base class constructor the first thing it does. AS3 allowed you to do it at any point you liked.
#2 by jackson on July 28th, 2014 ·
One of the first articles I wrote for this site was Super Is (Really) Optional!. It showed that you can actually not not make the
super()
call at all.Even without that, not calling
super()
right at the start of your constructor can often have strange side-effects. This is why C++, C#, and Java all require it. Here’s an example in AS3:When
MyStore
is constructed it starts by calling theStore
class’numProducts
getter. But this is beforeMyStore
callssuper()
so theStore
class’ constructor hasn’t run yet to initialize theproducts
field variable. This means that thenumProducts
getter tries to get thelength
property of anull
field variable and consequently throws an exception.The super class in AS3 can’t be implemented in a way that assumes its constructor has been called before the other functions. To be safe, you’d have to add a lot of checks to all your functions and then implement alternate functionality in the case that the class hasn’t had its constructor called yet (or ever!). C++, C#, and Java all force you to call the super/base class constructor and to call it first for just this very reason. It can be annoyingly rigid at times if you’re coming from AS3, but there is a rationale behind it that can be very helpful.
#3 by zeh on July 28th, 2014 ·
One caveat that baffled me when I first encountered it with C# is that if your class re-implements a base method, and then code in the base class that is not extended in the new class calls that method (or if you call that method yourself with an object cast to the base type), it’ll call the base class version of the method rather than the extended version. I never had that problem in ActionScript, since it always calls the, err, “bottom-most” method available for that object. So take this C# code:
The result is this:
The solution is to mark the base as
virtual
, or the overriding method asoverride
(using nothing implies using thenew
keyword, which creates a new method without overriding – an overload of sorts). So you have to write it like this:Which produces this:
Disclaimer: just wrote the code in comments with copying from my snippets so not sure it compiles, but the problem exists.
#4 by jackson on July 28th, 2014 ·
I wouldn’t really call it a problem, but rather a different way to design the language. There are advantages to allowing non-virtual functions as C# does. I’ll almost certainly cover this some day, but for now I’ve planned to discuss the syntax of
virtual
,override
,sealed
, andabstract
functions in part four of this series. Stay tuned!#5 by Tommy on July 28th, 2014 ·
Well isn’t that the same as in AS3?
If you have a method with a similar definition as a parent method you have to mark it as override or the compiler will print some error message, no?
#6 by zeh on July 28th, 2014 ·
In AS3, you have to mark it as override, and it won’t compile otherwise. But the thing is, in C# you don’t have to, it will still compile. But if you don’t mark it a proper
virtual
/override
(by omitting it, or usingnew
) it won’t behave the same way as AS3. You have to be more explicit, not because of compilation errors, but because the implied behavior is different. In my case, it took a while to understand why my code that was compiling perfectly wasn’t behaving as expected.It’s not a problem, per se. It’s fine behavior. But just a caveat when coming from AS3 because of mismatched expectations.
#7 by bzor on October 15th, 2014 ·
looks like you have
var NumVertices:uint;
in the C# example that calls the parent constructor..#8 by jackson on October 15th, 2014 ·
I’ve updated the article with a fix. Thanks for letting me know!
#9 by mt on March 19th, 2016 ·
setting the color seems to be missing here.. right?
#10 by jackson on March 19th, 2016 ·
Thanks for pointing this out. I’ve updated the article to set the
Color
field in the constructor to match the AS3 version.#11 by Arby on March 25th, 2017 ·
I think you made a typo up where you explain interface implementation:
“In C#, we see that : is used in place of the implements keyword just as it was with extends.”
I think you meant to say that a COMMA is used. Not :
#12 by jackson on March 26th, 2017 ·
We’re actually both right because either comma or colon can be used. Here’s the colon version:
But if there’s already one base class or interface being extended, then the comma is used:
#13 by Joe on May 31st, 2017 ·
Thanks for this. It’s amazing. I’m an ex-AS3 dev getting into Unity. This about the best help I could possibly ask for. The AS3 examples are solid. You’re clearly used to writing good quality AS3 code. This gives me confidence you’re showing me good practices for C#.
Thanks for taking the time!