Pull to refresh

С# для AS3 разработчиков. Часть 2: Расширение классов и реализация Интерфейсов

Reading time 4 min
Views 7.4K
Original author: Jackson Dunstan
image

Перевод статьи From AS3 to C#, Part 2: Extending Classes and Implementing Interfaces

Эта статья продолжает серию "C# для AS3 разработчиков", и сегодня мы рассмотрим устройство классов в C# с точки зрения AS3 разработчика (наследование классов, реализация интерфейсов и наследование интерфейсов).

Начнём с наследования классов. Вот, как это выглядит в AS3:

class Shape
{
}
 
class Circle extends Shape
{
}


А вот, как это выглядить в C#:

class Shape
{
}
 
class Circle : Shape
{
}


Как вы могли заметить, вместо ключевого слова extends, используется: (двоеточие).

Похожим образом обстоят дела с реализацией интерфейсов. Как это выглядит в 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 рассчитать
    }
 
    function GetSmoothness(): int
    {
        return 44; // TODO рассчитать
    }
}


Та же самая структура кода, но в C#:

interface IHasArea
{
    int GetArea();
}
 
interface IRound
{
    int GetSmoothness();
}
 
class Shape
{
}
 
class Circle : Shape, IHasArea, IRound
{
    int GetArea()
    {
        return 33; // TODO рассчитать
    }
 
    int GetSmoothness()
    {
        return 44; // TODO рассчитать
    }
}


В С#, двоеточие используется вместо ключевого слова implements, по такому же принципу, как и с extends. Описание интерфейсов в C# похоже на описание оных в AS3.

Пример расширения интерфейсов в AS3:

interface ISuper
{
}
 
interface ISub extends ISuper
{
}


Скорее всего, вы уже догадываетесь, как этот код выглядит в C#…

interface ISuper
{
}
 
interface ISub : ISuper
{
}


Давайте разберёмся, как описываются отношения между родительскими и дочерними классами/интерфейсами в C#. В AS3 вы можете использовать ключевые слова this и super в любых не статических функциях, чтобы обратиться к экземпляру текущего класса (this) или к экземпляру родительского класса (super). Например:

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 + ")";
    }
}


В данном примере, ключевое слово super обращается исключительно к переменным и функциям класса Polygon. Такой способ обращения к функциям используется крайне редко, но иногда он может помочь избежать в коде двусмысленностей, относительно того, какая из функций printDescription будет вызвана. Если бы в данном примере не использовалось слово super, то вызывалась бы функция printDescription класса Triangle, т.к. поиск в текущем классе происходит раньше родительского класса.

Ключевое слово this обращается исключительно к переменным и функциям класса Triangle. В примере, this используется, чтобы компилятор понимал, что нужно обрщаться к переменной color класса Triangle, а не к одноимённой переменной, передаваемой в конструктор.

А теперь версия на C#:

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 + ")";
    }
}


Эти два примера очень похожи, за исключением того, что в C#, вместо ключевого слова super, используется ключевое слово base. В C# base выполняет те же функции, что и super в AS3.

Другой пример использования super в AS3 (вызов родительского конструктора):

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


C# версия:

class Polygon
{
    uint NumVertices;
 
    Polygon(uint numVertices)
    {
        NumVertices = numVertices;
    }
}
 
class Triangle : Polygon
{
    String Color;
 
    Triangle(uint numVertices, String color)
        : base(numVertices)
    {
    }
}


Помимо того, что мы снова заменили super на base, вы можете заметить, что вызов происходит до выполнения кода в конструкторе (до фигурных скобок {}). Так же, вызову конструктора родительского класса предшествует двоеточие (:). Обратите внимание, что в конце строки вызова, не ставится точка с запятой (;).

Давайте разберём, как в AS3 сделать так, чтобы класс нельзя было расширить:

final class FinalClass
{
}
 
// Этот класс не будет компилироваться
class DerviedClass extends FinalClass
{
}


Похожий функционал в C#:

sealed class FinalClass
{
}
 
// Этот класс не будет компилироваться
class DerviedClass : FinalClass
{
}


В данном случае различия между C# и AS3 состоят только в названии ключевых слов. В C# класс, котоырй нельзя расширить обозначается ключевым словом sealed, а в AS3 для этого используется final, но эффект — абсолютно одинаковый.

В завершении, сравнение описанных особенностей C# и AS3 кода:
////////
// 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
{
}


На этом мы сегодня закругляемся. В следующей статье мы поговорим о геттерах и сеттерах (getter/setter) и закончим описание основ классов. После чего, мы сможем перейти к нюансам C#, аналогов которых нет в AS3.
Tags:
Hubs:
+6
Comments 4
Comments Comments 4

Articles