接口 (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 类或 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(){}
};
实现接口属性和事件
如上例所示,你可以使用普通虚拟属性实现接口属性。 你还可以在实现类中提供自定义 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; }
}
如果接口声明仅有 getter 或仅有 setter 的属性,则实现类应显式提供 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;
};
还可以在实现类中为事件实现自定义添加和移除方法。
显式接口实现
在 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; };
泛型接口必须遵循控制可访问性、成员、 需要关系、基类等内容的标准接口规则。
泛型接口可以采用前面带有
typename
或class
的一个或多个泛型类型参数。 不支持非类型参数。类型参数可以是任何 Windows 运行时类型。 即,类型参数可以是引用类型、值类型、接口类、委托、基本类型或公共枚举类。
封闭式泛型接口是从泛型接口继承的接口,并对所有类型形参指定具体的类型实参。 它可以在可使用非泛型私有接口的任意位置使用。
开放式泛型接口是具有一个或多个尚未为其提供具体类型的类型参数的接口。 它可以在可使用类型的任意位置使用,包括用作另一个泛型接口的类型参数。
可以只参数化整个接口,而不是单个方法。
不能约束类型参数。
封闭式泛型接口具有隐式生成的 UUID。 用户不能指定 UUID。
在接口中,对当前接口的任何引用(在方法参数、返回值或属性中)都假定引用当前实例化。 例如‘IMyIntf 表示 IMyIntf<T>。
当方法参数的类型是类型参数时,该参数或变量的声明将使用类型参数的名称,而不带任何指针、本机引用或句柄声明符。 换言之,绝不会写入“T^”。
模板化的 ref 类必须是私有的。 它们可以实现泛型接口,并将模板参数 T 传递到泛型参数 T。模板化 ref 类的每个实例化本身就是一个 ref 类。