📜 ⬆️ ⬇️

C # for AS3 developers. Part 5: Static Classes, Destructors, and Techniques for Working with Constructors

image

Translation of the article From AS3 to C #, Part 5: Static Classes, Destructors, and Constructor Tricks

Last time, we discussed abstract classes, but this week we will discuss even more abstract type of classes (than abstract classes): static classes. Also, we will look at the C # anti-constructors, which are better known as “destructors”, and, in addition to everything, we will look at some funny tricks when working with class constructors.

')
Static classes

Let's start today's article with “even more abstract” classes: static classes. Working with abstract classes, you can still extend them and create instances of child classes:

abstract class Shape { } class Square : Shape // legal { } new Shape(); // illegal new Square(); // legal 


When working with static classes, you can neither instantiate nor inherit them. You can never create an instance of this class:

 static class Shape { } class Square : Shape // illegal { } new Shape(); // illegal new Square(); // illegal 


But why do you need such classes at all? Such classes can be a good place to store static functions, fields, and properties. And, since you cannot create instances of such classes, the use of non-static fields of any data types is prohibited. Constructors of class instances are also prohibited, since class is automatically equated to sealed classes. A rather popular example of using such classes is the Math class. You will hardly ever need to create an instance of this class, but inside it contains a large number of useful static functions (for example, Abs) and fields (for example, PI). Here is what the implementation of this class might look like:

 public static class Math { // remember that 'const' is automatically static // also, this would surely have more precision public const double PI = 3.1415926; public static double Abs(double value) { return value >= 0 ? value : -value; } } new Math(); // illegal 


In AS3, by default there is no support for static classes at the compilation stage, but you can bypass this restriction using checks at the stage of playback (run-time). All you need to do is declare the class as final, and always throw an error in the constructor of this class:

 public final class Math { public static const PI:Number = 3.1415926; public function Math() { throw new Error("Math is static"); } public static function abs(value:Number): Number { return value >= 0 ? value : -value; } } new Math(); // legal, but throws an exception 


Destructors

The next item in today's program is destructors, who are “anti-constructors,” because they are responsible for the destruction of the class, and not for its creation, as is the case with conventional designers. Destructors are called by the garbage collector (Garbage Collector) just before the object frees up the memory it occupies. Here is what they look like:

 class TemporaryFile { ~TemporaryFile() { // cleanup code goes here } } 


To create a destructor, just add ~ to the class name. There can be only one destructor, and access modifiers cannot be used with it. Normally, there is no need to create destructors, but in some cases they can be useful as a way to clean up resources after using a class. In the example below, the destructor is used to remove a temporary file from the operating system, which in another case will not be deleted, because GC will never do this:

 using System.IO; class TemporaryFile { public String Path { get; private set; } TemporaryFile(String path) { Path = path; File.Create(path); } ~TemporaryFile() { File.Delete(Path); } } // Create the temporary file TemporaryFile temp = new TemporaryFile("/path/to/temp/file"); // ... use the temporary file // Remove the last reference to the TemporaryFile instance // GC will now collect temp, call the destructor, and delete the file temp = null; 


In this example, the TemporaryFile class creates a file in the class instance constructor, and deletes the file when there are no references to the class instance and the class is ready to be compiled by the GC to free up memory. In AS3, there are no functions that would be called when an instance of the class is ready to be compiled by the GC. Usually, to implement this behavior, you must manually create and call "pseudo-destructors" (usually called dispose or destroy):

 import flash.filesystem; class TemporaryFile { private var _path:String; public function get path(): String { return _path; } public function set path(p:String): void { _path = p; } private var _file:File; function TemporaryFile(path:String) { _path = path; _file = new File(path); var stream:FileStream = new FileStream(); stream.open(_file, FileMode.WRITE); } function dispose(): void { _file.deleteFile(); } } // Create the temporary file var temp:TemporaryFile = new TemporaryFile("/path/to/temp/file"); // ... use the temporary file // Manually call dispose() to delete the temporary file temp.dispose(); // Remove the last reference to the TemporaryFile instance // GC will now collect temp temp = null; 


Tricks when working with designers

The last topic today will be tricks when working with designers. We have already analyzed the way to call the base class constructor using the base keyword (similar to using the super keyword in AS3):

 class Polygon { Polygon(int numSides) { } } class Triangle : Polygon { Triangle() : base(3) // call the Polygon constructor { } } 


We also considered the possibility of creating more than one constructor using “overload”:

 class Vector3 { double X; double Y; double Z; Vector3() { X = 0; Y = 0; Z = 0; } Vector3(double x, double y, double z) { X = x; Y = y; Z = z; } Vector3(Vector3 vec) { X = vec.X; Y = vec.Y; Z = vec.Z; } } Vector3 v1 = new Vector3(); // (0, 0, 0) Vector3 v2 = new Vector3(1, 2, 3); // (1, 2, 3) Vector3 v3 = new Vector3(v2); // (1, 2, 3) 


Usually this method leads to duplication of code within constructors. But, since the version of the constructor that takes 3 parameters is the most common of all, then you can simply call it from 2 other constructors:

 class Vector3 { double X; double Y; double Z; Vector3() : this(0, 0, 0) { } Vector3(double x, double y, double z) { X = x; Y = y; Z = z; } Vector3(Vector3 vec) : this(vec.X, vec.Y, vec.Z) { } } Vector3 v1 = new Vector3(); // (0, 0, 0) Vector3 v2 = new Vector3(1, 2, 3); // (1, 2, 3) Vector3 v3 = new Vector3(v2); // (1, 2, 3) 


We can use this () to call other constructors within our class (similar to base (), which allowed us to call the parent class constructor). And again, in AS3 there was no such functionality by default, so it had to be “emulated” with the help of static pseudo-constructors, which called functions like init / setup / contruct for the objects being created:

 class Vector3 { var x:Number; var y:Number; var z:Number; function Vector3() { init(0, 0, 0); } // pseudo-constructor static function fromComponents(x:Number, y:Number, z:Number) { var ret:Vector3 = new Vector3(); ret.init(x, y, z); return ret; } // pseudo-constructor static function fromVector(Vector3 vec) { var ret:Vector3 = new Vector3(); ret.init(vec.X, vec.Y, vec.Z); return ret; } // helper function function init(x:Number, y:Number, z:Number): void { this.x = x; this.y = y; this.z = z; } } var v1:Vector3 = new Vector3(); // (0, 0, 0) var v2:Vector3 = Vector3.fromComponents(1, 2, 3); // (1, 2, 3) var v3:Vector3 = Vector3.fromVector(v2); // (1, 2, 3) 


On this we finish today and, as usual, at the end of the article we will compare the features of working with C # and AS3 described today:

 //////// // C# // //////// // Static class public static class MathHelpers { public const double DegreesToRadians = Math.PI / 180.0; public const double RadiansToDegrees = 180.0 / Math.PI; public static double ConvertDegreesToRadians(double degrees) { return degrees * DegreesToRadians; } public static double ConvertRadiansToDegrees(double radians) { return radians * RadiansToDegrees; } } // Class with a destructor class TemporaryFile { public String Path { get; private set; } TemporaryFile(String path) { Path = path; File.Create(path); } // Destructor ~TemporaryFile() { File.Delete(Path); } } // Class with shared constructor code class Vector3 { double X; double Y; double Z; Vector3() : this(0, 0, 0) { } // shared constructor code Vector3(double x, double y, double z) { X = x; Y = y; Z = z; } Vector3(Vector3 vec) : this(vec.X, vec.Y, vec.Z) { } } 

 ///////// // AS3 // ///////// // Static class - runtime only public class MathHelpers { public static const DegreesToRadians:Number = Math.PI / 180.0; public static const RadiansToDegrees:Number = 180.0 / Math.PI; public function MathHelpers() { throw new Error("MathHelpers is static"); } public static function ConvertDegreesToRadians(degrees:Number): Number { return degrees * DegreesToRadians; } public static function ConvertRadiansToDegrees(radians:Number): Number { return radians * RadiansToDegrees; } } // Class with a destructor class TemporaryFile { private var _path:String; public function get path(): String { return _path; } public function set path(p:String): void { _path = p; } private var _file:File; function TemporaryFile(path:String) { _path = path; _file = new File(path); var stream:FileStream = new FileStream(); stream.open(_file, FileMode.WRITE); } // Destructor - must be called manually function dispose(): void { _file.deleteFile(); } } // Class with shared constructor code class Vector3 { var x:Number; var y:Number; var z:Number; function Vector3() { init(0, 0, 0); } static function fromComponents(x:Number, y:Number, z:Number) { var ret:Vector3 = new Vector3(); ret.init(x, y, z); return ret; } static function fromVector(Vector3 vec) { var ret:Vector3 = new Vector3(); ret.init(vec.X, vec.Y, vec.Z); return ret; } // shared constructor code - helper function required function init(x:Number, y:Number, z:Number): void { this.x = x; this.y = y; this.z = z; } } 

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


All Articles