介面 (C++/CX)
雖然 ref 類別最多可以繼承自一個具象基底類別,但它可以實作任意數目的介面類別。 介面類別 (或介面結構) 本身可以繼承 (或要求) 多個介面類別、可以多載其成員函式,也可以具有類型參數。
特性
介面具有這些特性:
您必須在命名空間中宣告介面類別 (或結構),且此介面類別 (或結構) 可以具有公用或私用存取範圍。 只有公用介面會發出至中繼資料。
介面的成員可以包括屬性、方法和事件。
所有介面成員都是隱含公用及虛擬的成員。
不允許使用欄位和靜態成員。
做為屬性、方法參數或傳回值的類型只能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);
};
}
為了實作介面,由 ref 類別或 ref 結構宣告並實作虛擬方法和屬性。 此介面和實作的 ref 類別必須使用相同的方法參數名稱,如下列範例所示:
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:
//...
};
介面繼承階層架構
介面可以繼承一或多個介面。 但是,不同於 ref 類別或結構,介面不會宣告繼承的介面成員。 如果介面 B 繼承自介面 A,而 ref 類別 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(){}
};
實作介面屬性和事件
如上述範例所示,您可以使用 trivial 虛擬屬性來實作介面屬性。 您也可以在實作類別中提供自訂 getter 和 setter。 Getter 和 setter 都必須是公用的介面屬性。
//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; }
}
如果介面宣告 get-only 或 set-only 屬性,則實作的類別應該明確地提供 getter 或 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;
};
您也可以在實作的類別中,實作事件的自訂 add 和 remove 方法。
明確介面實作
當 ref 類別實作多個介面,且這些介面的方法具有與編譯器相同的名稱和簽章時,您可以使用下列語法明確表示實作類別方法的介面方法。
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 檔案中的用戶端程式碼無法實作該類別。 此類別可由相同元件中的非公用 ref 類別實作。 公用 ref 類別可以包含泛型介面類型做為私用成員。下列程式碼片段示範如何宣告泛型
interface class
,然後在私用 ref 類別中實作,並使用 ref 類別做為公用 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; };
泛型介面必須遵循規範存取範圍、成員、「 需要 」(Requires) 關聯性、基底類別等的標準介面規則。
泛型介面可以接受一或多個前面有
typename
或class
的泛型類型參數。 不支援非類型參數。類型參數可以是任何Windows 執行階段類型。 也就是說,類型參數可以是參考類型、實值類型、介面類別、委派、基礎類型或公用列舉類別。
「 封閉式泛型介面 」(Closed Generic Interface) 是繼承自泛型介面,並指定所有類型參數之具象類型引數的介面。 只要可以使用非泛型私用介面的地方,都能使用此介面。
「 開放式泛型介面 」(Open Generic Interface) 是其中有一或多個類型參數類型,尚未提供具象類型的介面。 只要可以使用類型的地方,都能使用此類型,包括做為另一個泛型介面的類型引數。
您只能參數化整個介面,不能參數化個別的方法。
類型參數不可受到條件約束。
封閉式泛型介面具有隱含產生的 UUID。 使用者無法指定 UUID。
在介面中,對目前介面的所有參考 (包括在方法參數、傳回值或屬性中) 都假設為參考目前的具現化。 例如,IMyIntf 表示 IMyIntf < T > 。
當方法參數的類型是類型參數時,該參數或變數的宣告會使用類型參數的名稱,而不需要任何指標、原生參考或控制碼宣告子。 換句話說,您不需撰寫 "T^"。
樣板化 ref 類別必須是私用類別。 它們可以實作泛型介面,並將範本參數 T 傳遞至泛型引數 T 。樣板化 ref 類別的每個具現化本身都是 ref 類別。