Интерфейсы (C++/CX)

Хотя класс ссылки может наследовать не более чем от одного конкретного базового класса, он может реализовывать любое количество классов интерфейсов. Сам класс интерфейса (или структура интерфейса) может наследовать (или требовать) несколько классов интерфейсов, может перегружать свои функции-члены и иметь параметры-типы.

Характеристики

Интерфейс имеет следующие характеристики:

  • Класс интерфейса (или структура) должен быть объявлен в пространстве имен, а также может иметь режим доступа public (открытый) или private (закрытый). Только открытые интерфейсы формируют метаданные.

  • члены интерфейса могут включать в себя свойства, методы и события;

  • Все члены интерфейса неявно являются открытыми и виртуальными.

  • поля и статические члены запрещены;

  • Типы, используемые в качестве свойств, параметров метода или возвращаемых значений, могут быть только среда выполнения Windows типами; это включает основные типы и типы классов перечисления.

Объявление и использование

В следующем примере показан способ объявления интерфейса. Обратите внимание, что интерфейс можно объявить или как класс, или как тип структуры.

namespace InterfacesTest
{
    public enum class PlayState {Playing, Paused, Stopped, Forward, Reverse};

    public ref struct MediaPlayerEventArgs sealed
    {
        property PlayState oldState;
        property PlayState newState;
    };

    public delegate void OnStateChanged(Platform::Object^ sender, MediaPlayerEventArgs^ a);
    public interface class IMediaPlayer // or public interface struct IMediaPlayer 
    {
        event OnStateChanged^ StateChanged;
        property Platform::String^ CurrentTitle;
        property PlayState CurrentState;
        void Play();
        void Pause();
        void Stop();
        void Back(float speed);
        void Forward(float speed);
    };
}

Для реализации интерфейса класс ссылки или структура ссылки объявляет и реализует виртуальные методы и свойства. Интерфейс и реализующий его класс ссылки должны использовать одинаковые имена параметров методов, как показано в следующем примере:

public ref class MyMediaPlayer sealed : public IMediaPlayer
{
public:
    //IMediaPlayer
    virtual event OnStateChanged^ StateChanged;
    virtual property Platform::String^ CurrentTitle;
    virtual property PlayState CurrentState;
    virtual void Play()
    {
        // ...
        auto args = ref new MediaPlayerEventArgs(); 
        args->newState = PlayState::Playing;
        args->oldState = PlayState::Stopped;
        StateChanged(this, args);
    }
    virtual void Pause(){/*...*/}
    virtual void Stop(){/*...*/}
    virtual void Forward(float speed){/*...*/}
    virtual void Back(float speed){/*...*/}
private:
    //...
};

Иерархии наследования интерфейсов

Интерфейс может наследовать от одного или нескольких интерфейсов. Но в отличие от структуры и класса ссылки, интерфейс не объявляет наследуемые члены интерфейса. Если интерфейс B наследует от интерфейса A, а класс ссылки C наследует от интерфейса B, класс C должен реализовывать и A, и B. Это показано в следующем примере.

public interface struct A { void DoSomething(); };
public interface struct B : A { void DoSomethingMore();};

public ref struct C sealed : B
{
    virtual void DoSomething(){}
    virtual void DoSomethingMore(){}
};


Реализация свойств и событий интерфейса

Как показано в предыдущем примере, для реализации свойств интерфейса можно использовать тривиальные виртуальные свойства. Также можно определить пользовательские методы получения и задания в реализующем классе. Оба эти метода должны быть открытыми в свойстве интерфейса.

//Alternate implementation in MediaPlayer class of IMediaPlayer::CurrentTitle
virtual property Platform::String^ CurrentTitle
{
    Platform::String^ get() {return "Now playing: " + _title;}
    void set(Platform::String^ t) {_title = t; }
}

Если интерфейс объявляет свойство, доступное только для получения или только для задания, реализующий класс должен явно предоставить метод получения или задания.

public interface class IMediaPlayer
{
    //...
    property Platform::String^ CurrentTitle
    {
        Platform::String^ get();           
    }
};

public ref class MyMediaPlayer3 sealed : public IMediaPlayer
{
public:
    //...
    virtual property Platform::String^ CurrentTitle
    {
        Platform::String^ get() {return "Now playing: " + _title;}
    }
private:
    Platform::String^ _title;
};

Кроме того, можно реализовать пользовательские методы добавления и удаления для событий в реализующем классе.

Явная реализация интерфейса

Если класс ссылки реализует несколько интерфейсов, и эти интерфейсы содержат методы, имена и сигнатуры которых идентичны компилятору, можно использовать следующий синтаксис для явного указания метода интерфейса, который реализуется методом класса.

public interface class IArtist
{     
    Platform::String^ Draw();
};

public interface class ICowboy
{
    Platform::String^ Draw();
};

public ref class MyClass sealed : public IArtist, ICowboy
{
public:     
    MyClass(){}     
    virtual  Platform::String^ ArtistDraw() = IArtist::Draw {return L"Artist";}
    virtual  Platform::String^ CowboyDraw() = ICowboy::Draw {return L"Cowboy";}
};

Универсальные интерфейсы

В C++/CX generic ключевое слово используется для представления параметризованного типа среда выполнения Windows. Параметризованный тип передается в метаданные и может использоваться кодом, который написан на любом языке, поддерживающем параметры-типы. Среда выполнения Windows определяет некоторые универсальные интерфейсы, например Windows::Foundation::Collections::IVector<T>, но не поддерживает создание общедоступных пользовательских универсальных интерфейсов в C++/CX. Однако можно создавать закрытые универсальные интерфейсы.

Вот как можно использовать среда выполнения Windows типы для создания универсального интерфейса:

  • Универсальный определяемый пользователем interface class в компоненте не может передаваться в соответствующий файл метаданных Windows; поэтому он не может быть открытым и не может реализовываться клиентским кодом в других файлах WinMD. Он может быть реализован неоткрытыми классами ссылок в том же компоненте. Открытый класс ссылки может иметь универсальный тип интерфейса в качестве закрытого члена.

    В следующем фрагменте кода показано, как объявить универсальный interface class , а затем реализовать его в закрытом классе ссылки и использовать класс ссылки в качестве закрытого члена открытого класса ссылки.

    public ref class MediaFile sealed {};
    
    generic <typename T>
    private interface class  IFileCollection
    {
        property Windows::Foundation::Collections::IVector<T>^ Files;
        Platform::String^  GetFileInfoAsString(T file);
    };
    
    private ref class MediaFileCollection : IFileCollection<MediaFile^>
    {
    public:
        virtual property Windows::Foundation::Collections::IVector<MediaFile^>^ Files;
        virtual Platform::String^  GetFileInfoAsString(MediaFile^ file){return "";}
    };
    
    public interface class ILibraryClient
    {
        bool FindTitle(Platform::String^ title);       
        //...
    };
    
    public ref class MediaPlayer sealed : public IMediaPlayer, public ILibraryClient
    {
    public:
        //IMediaPlayer
        virtual event OnStateChanged^ StateChanged;
        virtual property Platform::String^ CurrentTitle;
        virtual property PlayState CurrentState;
        virtual void Play()
        {
            auto args = ref new MediaPlayerEventArgs(); 
            args->newState = PlayState::Playing;
            args->oldState = PlayState::Stopped;
            StateChanged(this, args);
        }
        virtual void Pause(){/*...*/}
        virtual void Stop(){/*...*/}
        virtual void Forward(float speed){/*...*/}
        virtual void Back(float speed){/*...*/}
    
        //ILibraryClient
        virtual bool FindTitle(Platform::String^ title){/*...*/ return true;}
    
    private:
        MediaFileCollection^ fileCollection;
    
    };
    
  • Универсальный интерфейс должен следовать стандартным правилам интерфейсов, регламентирующим возможности доступа, члены, отношения requires (требует), базовые классы и т. д.

  • Универсальный интерфейс может принимать один или несколько параметров универсальных типов, перед которыми указывается typename или class. Параметры, не являющиеся типами, не поддерживаются.

  • Параметр типа может быть любым среда выполнения Windows типом. Это означает, что параметр-тип может быть ссылочным типом, типом значения, классом интерфейса, делегатом, основным типом или открытым перечислимым классом.

  • Закрытый универсальный интерфейс — это интерфейс, наследующий от универсального интерфейса и указывающий аргументы конкретного типа для всех параметров-типов. Его можно использовать везде, где допускается использовать неуниверсальный закрытый интерфейс.

  • Открытый универсальный интерфейс — это интерфейс, имеющий один или несколько параметров-типов, для которых пока не предоставлено никаких конкретных типов. Его можно использовать везде, где допускается использовать типы, в том числе в качестве аргумента-типа другого универсального интерфейса.

  • Параметризовать можно только весь интерфейс, но не отдельные методы.

  • Параметры-типы нельзя ограничивать.

  • Закрытый универсальный интерфейс имеет неявно создаваемый UUID. Пользователь не может задать UUID.

  • Если в интерфейсе имеется какая-либо ссылка на текущий интерфейс (в параметре метода, возвращаемом значении или свойстве), предполагается, что она указывает на текущий экземпляр. Например, IMyIntf означает IMyIntf<T>.

  • Если тип параметра метода является параметром типа, объявление этого параметра или переменной использует имя параметра типа без указателя, собственной ссылки или деклараторов. Иначе говоря, невозможно написать "T^".

  • Шаблонные классы ссылок должны быть закрытыми. Они могут реализовывать универсальные интерфейсы и передавать параметр шаблона T в универсальный аргумент T. Каждое создание экземпляра шаблонного класса ссылок само по себе является классом ссылок.

См. также

Система типов
Справочник по языку C++/CX
Справочник по пространствам имен