С# для AS3 розробників. Частина 4: Абстрактні класи і функції

image

Переклад статті From AS3 to C#, Part 4: Abstract Classes and Functions

У цій статті ми нарешті почнемо розбиратися в нюансах C#, аналогів яких немає в AS3. І першим ділом ми розглянемо абстрактні класи і функції. В AS3 необхідно було придумувати обхідні шляхи, щоб вони працювали правильно на етапі виконання (run-time). Але C# надає можливість змусити їх працювати на етапі компіляції (compile-time), і сьогодні ми розберемо ці способи.


Статичні инициализаторы

Але, до цього, я б хотів розповісти про одну особливості AS3 класів, про яку я забув розповісти в попередніх статтях: статичні инициализаторы (static initializers), так само відомі як инициализаторы класу, конструктори класу або статичні конструктори. Це — функція, яка буде викликана автоматично, коли статичні поля класу повинні бути ініціалізується. Ось як це виглядало в 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++;
}
}


Статичні инициализаторы використовуються не часто, оскільки у нас є можливість оголошувати і ініціалізувати поля в один і той же час. Наприклад:

private static var NEXT_ID:int = 1;


Але, вони можуть бути корисні, якщо необхідно реалізувати більш складну логіку поведінки програми. У будь-якому випадку, ось, як це може бути реалізовано в C#:

class Person
{
private static int NextID;
private int id;
// static initializer:
static Person()
{
NextID = 1;
}
// instance constructor
Person()
{
id = NextID++;
}
}


Статичні инициализаторы в C# називаються статичними конструкторами" і працюють за аналогією зі звичайними конструкторами, але не для окремих екземплярів класів, а для всього класу. Синтаксис подібних конструкторів збігається з звичайними, але на початку оголошення конструктора додається ключове слово static. У даних конструкторів не може бути модифікаторів доступу (private, public тощо) і вони не можуть приймати вхідні параметри.

Абстрактні класи

А тепер, давайте поговоримо про абстрактних класах: це — такі класи, які не можуть бути инстанциированы безпосередньо. Щоб створити екземпляр абстрактного класу, вам необхідно буде створити не абстрактний клас, який буде успадковуватися від абстрактного, і инстанциировать це не абстрактний клас. За замовчуванням в AS3 немає подібного функціоналу на етапі компіляції, але, існує досить популярний спосіб обійти це обмеження:

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


В даному випадку створення ExtrudedShape безпосередньо все ще можливо, і такий код буде компілюватися:

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


Але на етапі виконання спрацює перевірка першого аргументу, що спричинить за собою появу помилки ArgumentError, і примірник ExtrudedShape не буде створений. Це станеться через те, що класи, не успадковані від ExtrudedShape не будуть мати доступу до protected константі HIDDEN_KEY, але, в той же час, класи-похідні від ExtrudedShape зможуть звертатися до цієї змінної для передачі в батьківський конструктор:

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


Це — досить ефективний спосіб реалізації абстрактних класів на етапі програвання, але C# надає можливість зробити всю роботу на етапі компіляції:

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


Зверніть увагу на використання ключового слова abstract спочатку класу. Воно означає, що компілятор не повинен дозволяти створення даного класу безпосередньо. Даний підхід не вимагає додаткового коду або «обхідних шляхів», які необхідні в AS3 (похідних класів не потрібно використовувати HIDDEN_KEY, а їх ініціалізація та оголошення виглядає точно так само, як і у інших класів):

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


Абстрактні функції

Абстрактні функції використовуються в тих випадках, коли необхідно зазначити, що реалізація певної функції, обов'язково повинна бути перевизначено у дочірньому класі. І знову, в AS3 немає можливості реалізувати подібне на етапі компіляції, але, як і у випадку з абстрактними класами, існує спосіб обійти це обмеження:

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


В даному прикладі клас ExtrudedShape не реалізує функціонал функції get area, так як він нічого не знає про неї. У даній версії, звернення до функції get area класу ExtrudedShape викличе помилку. Даний підхід дозволяє реалізувати абстрактні функції на етапі відтворення, але не на етапі компіляції. Наприклад, наступний код буде успішно компілюватися без реалізації функції get area:

class ExtrudedCircle extends ExtrudedShape
{
}


Замість цього, в C# ми можемо просто використовувати ключове слово abstract:

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


Це ж ключове слово буде використовуватись для звичайних функцій (не геттер/сетер):

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


Сьогодні ми обговорили абстрактні класи та функції, а так само статичні инициализаторы. Для закріплення, давайте порівняємо особливості реалізації цього функціоналу в C# і 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;
}
}



У наступній статті ми поговоримо про деструкторах, трюках при роботі із перевантаженням конструкторів і про багато іншого.

Залишайтеся з нами!

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

0 коментарів

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