С# для AS3 розробників. Частина 5: Статичні класи, Деструктори і Прийоми для роботи з конструкторами

image

Переклад статті From AS3 to C#, Part 5: Static Classes, Destructors, and Constructor Tricks

Минулого разу ми з вами розглянули абстрактні класи, але вже на цьому тижні ми обговоримо навіть більш абстрактний тип класів (ніж абстрактні класи): статичні класи. Так само, ми розглянемо анти-конструктори C#, які більш відомі, як «деструктори», і, на додаток до всього, ми розглянемо деякі забавні трюки при роботі з конструкторами класів.


Статичні класи

Давайте почнемо сьогоднішню статтю з «навіть більш абстрактних класів: статичних класів. Працюючи з абстрактними класами, ви все ще можете розширювати їх і створювати екземпляри дочірніх класів:

abstract class Shape
{
}

class Square : Shape // legal
{
}

new Shape(); // illegal
new Square(); // legal


Працюючи зі статичними класами, ви не можете ні инстанциировать, ні наслідувати їх. Ви ніколи не зможете створити екземпляр такого класу:

static class Shape
{
}

class Square : Shape // illegal
{
}

new Shape(); // illegal
new Square(); // illegal


Але взагалі для чого можуть знадобитися такі класи? Подібні класи можуть бути хорошим місцем для зберігання статичних функцій, полів і властивостей. І, так як ви не можете створювати екземпляри подібних класів, в них заборонено використання не статичних полів будь-яких типів даних. Конструктори екземплярів класу так само заборонені, т. к. клас автоматично прирівнюється до sealed класів. Досить популярний приклад використання подібних класів — клас Math. Вам навряд чи коли-небудь потрібно буде створити екземпляр цього класу, але усередині нього міститься велика кількість корисних статичних функцій (наприклад Abs) і полів (наприклад PI). Ось як може виглядати реалізація подібного класу:

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


В AS3 за замовчуванням немає підтримки статичних класів на етапі компіляції, але ви можете обійти це обмеження, використовуючи перевірки на етапі програвання (run-time). Все, що вам потрібно буде зробити — це оголосити клас, як final, і завжди кидати помилку в конструкторі цього класу:

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


Деструктори

Наступним пунктом у сьогоднішній програмі йдуть деструктори, які є «анти-конструкторами», тому що вони відповідають за знищення класу, а не за його створення, як у випадку із звичайними конструкторами. Деструктори викликаються збирачами сміття (Garbage Collector) безпосередньо перед тим, як об'єкт звільняє займану їм пам'ять. Ось, як вони виглядають:

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


Для створення деструктора, досить додати ~ до імені класу. Деструктор може бути тільки один, і з ним не можна використовувати модифікатори доступу. Зазвичай, необхідність у створенні деструкторів немає, але в деяких випадках вони можуть бути корисними, як способу очищення ресурсів після використання класу. У прикладі нижче деструктор використовується для видалення з операційної системи тимчасового файлу, який в іншому випадку не буде видалений, т. к. GC ніколи не зробить цього:

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;


В даному прикладі клас TemporaryFile створює файл в конструкторі екземпляра класу, і видаляє файл, коли на екземпляр класу немає посилань і клас готовий бути зібраним GC, щоб звільнити пам'ять. В AS3 немає функцій, які викликалися, коли екземпляр класу готовий бути зібраним GC. Зазвичай, щоб реалізувати подібну поведінку, необхідно вручну створювати і викликати «псевдо-деструктори» (зазвичай їх називають розпоряджатися або 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;


Трюки при роботі з конструкторами

Останньою темою на сьогодні будуть трюки при роботі з конструкторами. Ми вже розбирали спосіб виклику конструктора базового класу, використовуючи ключове слово base (аналогічно до використання ключового слова super в AS3):

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


Так само, ми розглядали можливість створення більш ніж одного конструктора, використовуючи «перевантаження»:

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)


Зазвичай цей спосіб призводить до дублювання коду всередині конструкторів. Але, оскільки версія конструктора, яка приймає 3 параметра найбільш загальна із всіх, то можна просто викликати її з 2 інших конструкторів:

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)


Ми можемо використовувати this() для виклику інших конструкторів в рамках нашого класу (за аналогією з base(), що дозволяло викликати конструктор батьківського класу). І знову, в AS3 не було подібного функціоналу за замовчуванням, тому його доводилося «емулювати за допомогою статичних псевдо-конструкторів, які викликали функції зразок init/setup/contruct у створюваних об'єктів:

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)


На цьому сьогодні закінчимо і, як зазвичай, у завершенні статті ми порівняємо описані сьогодні особливості роботи з C# і AS3:

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


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.