Schnittstellen (C++/CX)

Obwohl eine Verweisklasse nur von einer konkreten Basisklasse erben kann, kann sie eine beliebige Anzahl von Schnittstellenklassen implementieren. Eine Schnittstellenklasse (oder Schnittstellenstruktur) kann (oder muss) mehrere Schnittstellenklassen erben, kann ihre Memberfunktionen überladen und darf Typparameter besitzen.

Merkmale

Eine Schnittstelle verfügt über die folgenden Merkmale:

  • Eine Schnittstellenklasse (oder Struktur) muss innerhalb eines Namespace deklariert werden und kann öffentliche oder private Zugreifbarkeit besitzen. Nur öffentliche Schnittstellen werden an Metadaten ausgegeben.

  • Die Member einer Schnittstelle können Eigenschaften, Methoden und Ereignisse enthalten.

  • Alle Schnittstellenmember sind implizit öffentlich und virtuell.

  • Felder und statische Member sind nicht zulässig.

  • Typen, die als Eigenschaften, Methodenparameter oder Rückgabewerte verwendet werden, können nur Windows-Runtime Typen sein. Dazu gehören die grundlegenden Typen und Enumerationsklassentypen.

Deklaration und Verwendung

Im folgenden Beispiel wird die Deklaration einer Schnittstelle erläutert. Beachten Sie, dass eine Schnittstelle entweder als Klassen- oder als Strukturtyp deklariert werden kann.

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

Um eine Schnittstelle zu implementieren, deklariert und implementiert eine Verweisklasse oder Verweisstruktur virtuelle Methoden und Eigenschaften. Die Schnittstelle und die implementierende Verweisklasse müssen dieselben Methodenparameternamen verwenden, wie im folgenden Beispiel gezeigt:

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

Schnittstellenvererbungshierarchien

Eine Schnittstelle kann von einer oder mehreren Schnittstellen erben. Im Gegensatz zu einer Verweisklasse oder -struktur, deklariert eine Schnittstelle nicht die geerbten Schnittstellenmember. Wenn Schnittstelle B von Schnittstelle A erbt und Verweisklasse C von B erbt, muss C sowohl A als auch B implementieren. Dies wird im nächsten Beispiel gezeigt.

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


Implementieren von Schnittstelleneigenschaften und -ereignissen

Wie im vorherigen Beispiel gezeigt, können Sie triviale virtuelle Eigenschaften verwenden, um Schnittstelleneigenschaften zu implementieren. Sie können auch benutzerdefinierte Getter und Setter in der implementierenden Klasse bereitstellen. Sowohl der Getter als auch der Setter müssen in einer Schnittstelleneigenschaft öffentlich sein.

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

Wenn eine Schnittstelle eine get-only- oder set-only-Eigenschaft deklariert, dann sollte die implementierende Klasse explizit einen Getter oder Setter bereitstellen.

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

Sie können in der implementierenden Klasse für Ereignisse auch benutzerdefinierte hinzufügen- und entfernen-Methoden implementieren.

Explizite Schnittstellenimplementierung

Wenn eine Verweisklasse mehrere Schnittstellen implementiert und diese Schnittstellen Methoden verfügen, deren Namen und Signaturen für den Compiler identisch sind, können Sie die folgende Syntax verwenden, um die Schnittstellenmethode explizit angeben, die eine Klassenmethode implementiert.

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

Generische Schnittstellen

In C++/CX wird die generic Schlüsselwort (keyword) verwendet, um einen Windows-Runtime parametrisierten Typ darzustellen. Ein parametrisierter Typ wird in Metadaten ausgegeben und kann durch jeden Code genutzt werden, der in einer Programmiersprache geschrieben ist, die Typparameter unterstützt. Die Windows-Runtime definiert einige generische Schnittstellen, z. B. Windows::Foundation::Collections::IVector<T>, unterstützt jedoch nicht die Erstellung öffentlicher benutzerdefinierter generischer Schnittstellen in C++/CX. Sie können jedoch private generische Schnittstellen erstellen.

Hier erfahren Sie, wie Windows-Runtime Typen zum Erstellen einer generischen Schnittstelle verwendet werden können:

  • Eine generische benutzerdefinierte interface class in einer Komponente kann nicht in ihre Windows-Metadatendatei ausgegeben werden. Sie kann daher keine öffentliche Zugreifbarkeit besitzen und nicht von Clientcode in anderen WINMD-Dateien implementiert werden. Sie kann durch nicht öffentliche Verweisklassen in der gleichen Komponente implementiert werden. Eine öffentliche Verweisklasse kann einen generischen Schnittstellentyp als privaten Member besitzen.

    Der folgenden Codeausschnitt zeigt, wie eine generische interface class deklariert und dann in einer privaten Verweisklasse implementiert wird, und wie die Verweisklasse als privater Member in einer öffentlichen Verweisklasse verwendet wird.

    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;
    
    };
    
  • Eine generische Schnittstelle muss den Standardregeln für Schnittstellen folgen, die Zugreifbarkeit, Member, erforderliche Beziehungen, Basisklassen usw. festlegen

  • Eine generische Schnittstelle kann eine oder mehrere generischen Typparameter verwenden, denen typename oder classvorangestellt werden. Nichttypenparameter werden nicht unterstützt.

  • Ein Typparameter kann ein beliebiger Windows-Runtime Typ sein. Das bedeutet, dass der Typparameter ein Verweistyp, ein Werttyp, eine Schnittstellenklasse, ein Delegat, ein fundamentaler Typ oder eine öffentliche Enumeratorklasse sein kann.

  • Eine geschlossene generische Schnittstelle ist eine Schnittstelle, die von einer generischen Schnittstelle erbt und konkrete Typargumente für alle Typparameter spezifiziert. Sie kann überall verwendet werden, wo eine nicht generische private Schnittstelle verwendet werden kann.

  • Eine geöffnete generische Schnittstelle ist eine Schnittstelle, die einen oder mehrere Typparameter aufweist, für die noch kein konkreter Typ bereitgestellt wurde. Sie kann überall verwendet werden, wo ein Typ verwendet werden kann. Dies schließt die Verwendung als Typargument einer anderen generischen Schnittstelle ein.

  • Sie können nur eine gesamte Schnittstelle, nicht einzelne Methoden parametrisieren.

  • Typparameter können nicht beschränkt werden.

  • Eine geschlossene generische Schnittstelle verfügt über einen implizit generierten UUID. Benutzer können den UUID nicht angeben.

  • In der Schnittstelle wird angenommen, dass jeder Verweis auf die aktuelle Schnittstelle – in einem Methodenparameter, einem Rückgabewert oder einer Eigenschaft – auf die aktuelle Instanziierung verweist. IMyIntf bedeutet z. B. IMyIntf<T.>

  • Wenn der Typ eines Methodenparameters ein Typparameter ist, verwendet die Deklaration dieses Parameters oder dieser Variablen den Namen des Typparameters ohne Zeiger, systemeigener Verweis oder Handle-Deklaratoren. Das heißt, schreiben Sie nie "T^".

  • Auf Vorlagen basierende Verweisklassen müssen privat sein. Sie können generische Schnittstellen implementieren und den Vorlagenparameter T an das generische Argument T übergeben. Jede Instanziierung einer vorlagenbasierten Verweisklasse ist selbst eine Verweisklasse.

Siehe auch

Typsystem
C++-/CX-Programmiersprachenreferenz
Referenz zu Namespaces