📜 ⬆️ ⬇️

C # for AS3 developers. Part 4: Abstract Classes and Functions

image

Translation of the article From AS3 to C #, Part 4: Abstract Classes and Functions

In this article, we will finally begin to understand the nuances of C #, which have no analogues in AS3. And first we will look at abstract classes and functions. In AS3, it was necessary to come up with workarounds so that they work correctly at runtime (run-time). But C # provides the ability to make them work at the compile-time (compile-time), and today we will analyze these methods.

')
Static initializers

But, before that, I would like to talk about one feature of the AS3 classes, which I forgot to talk about in previous articles: static initializers (static initializers), also known as class initializers, class constructors, or static constructors. This is a function that will be called automatically when static class fields need to be initialized. Here is how it looked 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 are not often used, because we have the ability to declare and initialize fields at the same time. For example:

 private static var NEXT_ID:int = 1; 


But, they can be useful if you need to implement a more complex logic of application behavior. In any case, here's how it can be implemented 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 “static constructors” and work by analogy with ordinary constructors, but not for individual instances of classes, but for the entire class. The syntax for such constructors is the same as usual, but the static keyword is added at the beginning of the constructor declaration. These constructors cannot have access modifiers (private, public, etc.) and they cannot accept incoming parameters.

Abstract classes

Now, let's talk about abstract classes: these are classes that cannot be instantiated directly. To create an instance of an abstract class, you will need to create a non-abstract class that will inherit from the abstract, and instantiate this non-abstract class. By default, in AS3 there is no such functionality at the compilation stage, but, there is a rather popular way to get around this limitation:

 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; } } 


In this case, creating an ExtrudedShape directly is still possible, and such code will be compiled:

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


But at the execution stage, the check of the first argument will work, which will lead to the appearance of the error ArgumentError, and the ExtrudedShape instance will not be created. This will happen because classes not inherited from ExtrudedShape will not have access to the protected HIDDEN_KEY constant, but at the same time, classes derived from ExtrudedShape will be able to access this variable for transmission to the parent constructor:

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


This is a fairly effective way to implement abstract classes at the playback stage, but C # provides the ability to do all the work at the compilation stage:

 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 at the beginning of the class. It means that the compiler should not allow the creation of this class directly. This approach does not require additional code or “workarounds” that are required in AS3 (derived classes do not need to use HIDDEN_KEY, and their initialization and declaration looks exactly the same as other classes):

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


Abstract functions

Abstract functions are used in cases where it is necessary to indicate that the implementation of a particular function must be redefined in a child class. And again, in AS3 there is no possibility to implement this at the compilation stage, but, as in the case of abstract classes, there is a way to get around this restriction:

 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; } } 


In this example, the ExtrudedShape class does not implement the functionality of the get area function, since it does not know anything about it. In this version, calling the ExtrudedShape class get area function will cause an error. This approach allows to realize abstract functions at the stage of reproduction, but not at the stage of compilation. For example, the following code will successfully compile without implementing the get area function:

 class ExtrudedCircle extends ExtrudedShape { } 


Instead, in C # we can 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 will be used for normal functions (not the getter / setter):

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


Today we discussed abstract classes and functions, as well as static initializers. To consolidate, let's compare the features of the implementation of this functionality in C # and AS3:

 //////// // 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; } } 



In the next article we will talk about destructors, tricks when working with constructor overload and much more.

Stay with us!

Source: https://habr.com/ru/post/249045/


All Articles