Udostępnij za pośrednictwem


Interfejsy (C++/CX)

Mimo że klasa ref może dziedziczyć z co najwyżej jednej klasy bazowej, może implementować dowolną liczbę klas interfejsów. Sama klasa interfejsu (lub struktura interfejsu) może dziedziczyć (lub wymagać) wielu klas interfejsu, może przeciążać funkcje składowe i może mieć parametry typu.

Charakterystyki

Interfejs ma następujące cechy:

  • Klasa interfejsu (lub struktura) musi być zadeklarowana w przestrzeni nazw i może mieć dostęp publiczny lub prywatny. Tylko interfejsy publiczne są emitowane do metadanych.

  • Elementy członkowskie interfejsu mogą zawierać właściwości, metody i zdarzenia.

  • Wszystkie elementy członkowskie interfejsu są niejawnie publiczne i wirtualne.

  • Pola i statyczne elementy członkowskie są niedozwolone.

  • Typy, które są używane jako właściwości, parametry metody lub zwracane wartości mogą być tylko środowisko wykonawcze systemu Windows typów; obejmuje to typy podstawowe i typy klas wyliczenia.

Deklaracja i użycie

W poniższym przykładzie pokazano, jak zadeklarować interfejs. Zwróć uwagę, że interfejs można zadeklarować jako klasę lub typ struktury.

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

Aby zaimplementować interfejs, klasa ref lub struktura ref deklaruje i implementuje metody wirtualne i właściwości. Interfejs i implementowanie klasy ref muszą używać tych samych nazw parametrów metody, jak pokazano w tym przykładzie:

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

Hierarchie dziedziczenia interfejsu

Interfejs może dziedziczyć z co najmniej jednego interfejsu. Jednak w przeciwieństwie do klasy ref lub struktury interfejs nie deklaruje odziedziczonych składowych interfejsu. Jeśli interfejs B dziedziczy z interfejsu A, a klasa ref C dziedziczy po B, C musi zaimplementować zarówno A, jak i B. Zostanie to pokazane w następnym przykładzie.

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(){}
};


Implementowanie właściwości i zdarzeń interfejsu

Jak pokazano w poprzednim przykładzie, można użyć trywialnych właściwości wirtualnych do zaimplementowania właściwości interfejsu. Można również udostępnić niestandardowe metody pobierania i ustawiacze w klasie implementowania. Zarówno getter, jak i setter muszą być publiczne we właściwości interfejsu.

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

Jeśli interfejs deklaruje właściwość get-only lub set-only, klasa implementowania powinna jawnie podać metodę getter lub setter.

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

Można również zaimplementować niestandardowe metody dodawania i usuwania zdarzeń w klasie implementowania.

Implementacja jawnego interfejsu

Gdy klasa ref implementuje wiele interfejsów, a te interfejsy mają metody, których nazwy i podpisy są identyczne z kompilatorem, można użyć następującej składni, aby jawnie wskazać metodę interfejsu implementaną przez metodę klasy.

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

Interfejsy ogólne

W języku C++/CX generic słowo kluczowe jest używane do reprezentowania typu środowisko wykonawcze systemu Windows sparametryzowanego. Typ sparametryzowany jest emitowany w metadanych i może być używany przez kod napisany w dowolnym języku, który obsługuje parametry typu. Środowisko wykonawcze systemu Windows definiuje niektóre interfejsy ogólne — na przykład Windows::Foundation::Collections::IVector<T> — ale nie obsługuje tworzenia publicznych interfejsów ogólnych zdefiniowanych przez użytkownika w języku C++/CX. Można jednak utworzyć prywatne interfejsy ogólne.

Poniżej przedstawiono sposób użycia typów środowisko wykonawcze systemu Windows do tworzenia interfejsu ogólnego:

  • Ogólny użytkownik zdefiniowany interface class w składniku nie może być emitowany do pliku metadanych systemu Windows. W związku z tym nie może mieć dostępu publicznego, a kod klienta w innych plikach winmd nie może go zaimplementować. Można ją zaimplementować za pomocą klas ref innych niż publiczne w tym samym składniku. Publiczna klasa ref może mieć ogólny typ interfejsu jako prywatny element członkowski.

    Poniższy fragment kodu pokazuje, jak zadeklarować rodzaj, interface class a następnie zaimplementować go w prywatnej klasie ref i użyć klasy ref jako prywatnej składowej w klasie publicznej ref.

    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;
    
    };
    
  • Interfejs ogólny musi być zgodny ze standardowymi regułami interfejsu, które zarządzają ułatwieniami dostępu, elementami członkowskimi, wymagają relacji, klas bazowych itd.

  • Interfejs ogólny może przyjmować co najmniej jeden ogólny parametr typu, który jest poprzedzony ciągiem typename lub class. Parametry inne niż typ nie są obsługiwane.

  • Parametr typu może być dowolnym typem środowisko wykonawcze systemu Windows. Oznacza to, że parametr typu może być typem odwołania, typem wartości, klasą interfejsu, delegatem, typem podstawowym lub publiczną klasą wyliczenia.

  • Zamknięty interfejs ogólny to interfejs, który dziedziczy z interfejsu ogólnego i określa konkretne argumenty typu dla wszystkich parametrów typu. Można go używać w dowolnym miejscu, w przypadku którego można użyć nieogólnego interfejsu prywatnego.

  • Otwarty interfejs ogólny to interfejs, który ma co najmniej jeden parametr typu, dla którego nie podano jeszcze żadnego konkretnego typu. Można go używać w dowolnym miejscu, w których można używać typu, w tym jako argument typu innego interfejsu ogólnego.

  • Można sparametryzować tylko cały interfejs, a nie poszczególne metody.

  • Nie można ograniczyć parametrów typu.

  • Zamknięty interfejs ogólny ma niejawnie wygenerowany identyfikator UUID. Użytkownik nie może określić identyfikatora UUID.

  • W interfejsie zakłada się, że wszystkie odwołania do bieżącego interfejsu — w parametrze metody, zwracanej wartości lub właściwości — odwołują się do bieżącego wystąpienia. Na przykład IMyIntf oznacza IMyIntf<T>.

  • Gdy typ parametru metody jest parametrem typu, deklaracja tego parametru lub zmiennej używa nazwy parametru typu bez żadnego wskaźnika, odwołania natywnego lub deklaratorów obsługi. Innymi słowy, nigdy nie piszesz "T^".

  • Klasy ref szablonów muszą być prywatne. Mogą implementować interfejsy ogólne i mogą przekazać parametr szablonu T do argumentu ogólnego T. Każde wystąpienie klasy ref szablonu jest samą klasą ref.

Zobacz też

System typów
Dokumentacja języka C++/CX
Dokumentacja przestrzeni nazw