Interfacce (C++/CX)

Sebbene possa ereditare solo da una classe base concreta, una classe di riferimento può implementare qualsiasi numero di classi di interfaccia. Una classe di interfaccia (o struct di interfaccia) può ereditare (o richiedere) più classi di interfaccia, eseguire l'overload delle relative funzioni membro e avere parametri di tipo.

Caratteristiche

Ecco le caratteristiche di un'interfaccia:

  • Una classe (o uno struct) di interfaccia può essere dichiarata in uno spazio dei nomi e avere accessibilità pubblica o privata. Solo le interfacce pubbliche vengono emesse nei metadati.

  • I membri di un'interfaccia possono includere proprietà, metodi ed eventi.

  • Tutti i membri di interfaccia sono implicitamente pubblici e virtuali.

  • Non sono consentiti campi e membri statici.

  • I tipi usati come proprietà, parametri di metodo o valori restituiti possono essere solo tipi Windows Runtime; sono inclusi i tipi fondamentali e i tipi di classe enumerazione.

Dichiarazione e utilizzo

Nell'esempio riportato di seguito viene illustrato come dichiarare un'interfaccia. Nota che un'interfaccia può essere dichiarata come tipo di classe o come tipo di struct.

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

Per implementare un'interfaccia, una classe o uno struct di riferimento dichiara e implementa proprietà e metodi virtuali. L'interfaccia e la classe di riferimento di implementazione devono utilizzare gli stessi nomi di parametro del metodo, come illustrato nel seguente esempio:

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

Gerarchie di ereditarietà dell'interfaccia

Un'interfaccia può ereditare da una o più interfacce. A differenza di una classe o di uno struct di riferimento, in un'interfaccia non vengono dichiarati i membri di interfaccia ereditati. Se l'interfaccia B eredita dall'interfaccia A e la classe di riferimento C eredita da B, C deve implementare sia A sia B, come mostrato nell'esempio seguente.

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


Implementazione di proprietà ed eventi di interfaccia

Come illustrato nell'esempio precedente, puoi utilizzare proprietà virtuali semplici per implementare le proprietà dell'interfaccia e puoi fornire metodi Get e Set personalizzati nella classe dell'implementazione. Entrambi i metodi Get e Set devono essere pubblici in una proprietà dell'interfaccia.

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

Se un'interfaccia dichiara una proprietà solo Get o solo Set, la classe di implementazione deve fornire esplicitamente un metodo Get o Set.

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

Puoi anche implementare metodi Add e Remove personalizzati per degli eventi nella classe di implementazione.

implementazione esplicita dell'interfaccia

Quando una classe di riferimento implementa più interfacce e queste ultime hanno metodi i cui nomi e le cui firme sono identici a quelli del compilatore, puoi utilizzare la sintassi riportata di seguito per indicare in modo esplicito il metodo di interfaccia di cui il metodo della classe esegue l'implementazione.

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

Interfacce generiche

In C++/CX la generic parola chiave viene usata per rappresentare un tipo con parametri di Windows Runtime. Un tipo con parametri viene emesso nei metadati e può essere utilizzato da codice scritto in qualsiasi linguaggio che supporta i parametri di tipo. Windows Runtime definisce alcune interfacce generiche, ad esempio Windows::Foundation::Collections::IVector<T>, ma non supporta la creazione di interfacce generiche pubbliche definite dall'utente in C++/CX. Tuttavia, è possibile creare interfacce generiche private.

Ecco come usare i tipi di Windows Runtime per creare un'interfaccia generica:

  • Una interface class generica definita dall'utente in un componente non può essere emessa nel relativo file di metadati Windows, pertanto non può avere accessibilità pubblica e il codice client in altri file con estensione winmd non può implementarla. Può essere implementata da classi di riferimento non pubbliche nello stesso componente. Una classe di riferimento pubblica può avere un tipo di interfaccia generico come membro privato.

    Nel codice riportato di seguito viene mostrato come dichiarare una interface class generica, implementarla in una classe di riferimento privata e utilizzare la classe di riferimento come membro privato in una classe di riferimento pubblica.

    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;
    
    };
    
  • Un'interfaccia generica deve rispettare le regole di interfaccia standard che regolano l'accessibilità, i membri, le relazioni obbligatorie , le classi di base e così via.

  • Questo tipo di interfaccia può accettare uno o più parametri di tipo generico che sono preceduti da typename o da class. Non sono supportati i parametri non di tipo.

  • Un parametro di tipo può essere qualsiasi tipo di Windows Runtime. Ciò significa che il parametro di tipo può essere un tipo di riferimento, un tipo di valore, una classe di interfaccia, un delegato, un tipo fondamentale o una classe enum pubblica.

  • Un' interfaccia generica chiusa è un'interfaccia che eredita da un'interfaccia generica e specifica gli argomenti di tipo concreti per tutti i parametri di tipo. Può essere utilizzata in tutti i casi in cui è consentito l'uso di un'interfaccia privata non generica.

  • Un' interfaccia generica aperta è un'interfaccia con uno o più parametri di tipo per i quali non è stato fornito ancora un tipo concreto. Può essere utilizzata ovunque sia consentito l'uso di un tipo e quindi anche come argomento di tipo di un'altra interfaccia generica.

  • È possibile parametrizzare solo l'intera interfaccia, non i singoli metodi.

  • I parametri di tipo non possono essere vincolati.

  • Un'interfaccia generica chiusa dispone di un UUID generato in modo implicito. Un utente non può specificare l'UUID.

  • Nell'interfaccia, si presuppone che qualsiasi riferimento all'interfaccia corrente, in un parametro di metodo, un valore restituito o una proprietà, faccia riferimento all'istanza corrente. Ad esempio, IMyIntf significa IMyIntf<T>.

  • Quando il tipo di un parametro di metodo è un parametro di tipo, la dichiarazione di tale parametro o variabile usa il nome del parametro di tipo senza puntatori, riferimenti nativi o dichiaratori di handle. In altre parole, non si scrive mai "T^".

  • Le classi di riferimento basate su modelli devono essere private, Possono implementare interfacce generiche e passare il parametro modello T all'argomento T generico. Ogni creazione di istanze di una classe di riferimento basato su modelli è una classe ref.

Vedi anche

Sistema di tipi
Riferimenti al linguaggio C++/CX
Riferimenti a spazi dei nomi