预定义的属性 (MIDL 3.0)

有许多预定义的自定义属性,用于控制编译器合成接口的 IID (名称和) 标识符。 这些属性允许你在精细级别控制类的版本控制和二进制 API。

如果你是组件开发人员和/或库作者,则你可能希望使用这些属性来确保组件从一个版本到下一个版本保持二进制稳定。

如果你是应用程序开发人员,则通常不需要使用这些属性,因为在修订类型后将重新编译应用程序。

属性[allowforweb]

有关属性的使用和用途的详细信息 allowforweb ,请参阅 AllowForWebAttribute 类

属性[constructor_name]

属性 constructor_name 指定包含构造函数成员的工厂接口的名称和 IID。 有关 工厂接口详细信息 ,请参阅合成接口。

注意

工厂接口仅适用于具有非默认构造函数的密封类,或者适用于具有默认和/或非默认构造函数的未密封类。

在下面的示例中,受保护的 Block 构造函数放置在 IBlockFactory 接口中,并且该接口具有指定的 IID。

[constructor_name("Windows.UI.Xaml.Documents.IBlockFactory", 07110532-4f59-4f3b-9ce5-25784c430507)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    protected Block();
    ...
}

因此,当类说明不引用接口,但需要接口来实现类时,MIDL 3.0 编译器会根据需要合成并添加接口。

属性[contentproperty]

属性 contentproperty 表示 ContentPropertyAttribute 类。 下面是一个示例:

// BgLabelControl.idl
namespace BgLabelControlApp
{
    [contentproperty("Content")]
    runtimeclass BgLabelControl : Windows.UI.Xaml.Controls.Control
    {
        BgLabelControl();
        static Windows.UI.Xaml.DependencyProperty LabelProperty{ get; };
        String Label;
        static Windows.UI.Xaml.DependencyProperty ContentProperty{ get; };
        IInspectable Content;
    }
}

属性[contract]

请勿在你自己的 contract API 中使用该特性;它只对内置 api Windows有意义。

contract属性指定 Windows 10 API 协定的名称和版本 (请参阅使用扩展 SDK 编程) 其中,特性化类型和/或成员首先引入 Windows (因此,对于未作为 Windows) 的一部分传递的 API 没有意义。 属性采用 形式 [contract(ContractName, ContractVersion)],它显示在它所应用到的项之前。

属性[default]

如果未指定默认接口,则 MIDL 3.0 编译器将选择第一个实例接口。 若要重写此选择,请将 属性 default 插入到要作为默认接口的接口之前。

// Declaring an external interface as the default
runtimeclass C : [default]I { ... }

// Declaring a specific exclusiveto interface as the default.
// This is very unusual.
runtimeclass C
{
    ...

    [default][interface_name(...)]
    {
        ...
    }
}

属性[default_interface]

属性 default_interface 用于强制生成默认接口,否则不会生成默认接口。 在下面的示例中, StateTriggerBase 不需要默认接口 (,因为它没有公共非状态成员) default_interface ,因此仅存在 属性,导致生成一个名为 IStateTriggerBase) 的 (。

[default_interface]
unsealed runtimeclass StateTriggerBase
{
    protected void SetActive(Boolean IsActive);
};

属性[default_overload]

注意

构造函数不支持此属性。 如果无法按 arity 重载构造函数,可以定义重载的工厂方法,如本节最后一个代码片段中显示的 CreateFromUriCreateFromStream 示例。

属性 default_overload 表示 DefaultOverloadAttribute 类,它指示方法是默认重载方法。 本部分将指导 你完成属性 的原因和使用 [default_overload] 准则。

可以按 arity Windows 运行时重载方法。 也就是说,只要每个方法采用不同数量的参数,就可以定义多个同名的方法。 出于可用性目的,建议在末尾添加新参数,并且短参数列表函数的行为等效于使用 (方案特定的) 自然默认值调用较长参数列表函数。

runtimeclass Widget
{
    void Start();
    void Start(StartMode mode);
    void Start(StartMode mode, Widget parent);
}

在以上示例中,可以使用两个参数调用 Start ,以指定启动模式和父小组件。 如果省略父小组件,则默认为 null。 如果省略模式,则它会在某些特定于方案的默认模式下启动。

下一 个示例使用 DeviceInformation.CreateWatcher

runtimeclass DeviceInformation
{
    static DeviceWatcher CreateWatcher();
    static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
    static DeviceWatcher CreateWatcher(String aqsFilter);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties,
                                       DeviceInformationKind kind);
}

该示例中有五个重载,其中大多数重载遵循建议的模式,即让每个新重载成为上一重载的扩展。

如果决定具有具有相同数量的参数的方法的多个重载,则收到编译器错误:

DeviceInformation.CreateWatcher 的 1 参数重载必须恰好具有一个指定为默认重载的方法,方法是使用 Windows。Foundation.Metadata.DefaultOverloadAttribute。

原因是某些编程语言是动态类型化。 JavaScript 和 Python 是两个示例。 对于这些语言,重载的选择仅考虑参数的数量,而不考虑其类型。 这意味着对 的 JavaScript 调用 DeviceInformation.createWatcher(v); 不明确— v 应强制转换为 DeviceClass 还是 String

若要解决多义性,需要将 属性 [default_overload] 应用于方法之一。 但如何选择哪一个?

应选择默认重载,以便非默认重载的功能仍然通过其他方式(通常通过其他重载之一)可用。

DeviceInformation.CreateWatcher 示例中,可以通过调用 String、IIterableString<> 重载并传递空的属性列表来获取 String 重载的功能。 另一方面, DeviceClass 重载是创建 筛选为 DeviceClass 的 DeviceWatcher 的唯一 方法

这使得 DeviceClass 方法成为明确的获胜者。 因此,通过向该重载 [default_overload] 应用 属性来指示这一点。

runtimeclass DeviceInformation
{
    static DeviceWatcher CreateWatcher();
    [default_overload]
    static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
    static DeviceWatcher CreateWatcher(String aqsFilter);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties,
                                       DeviceInformationKind kind);
}

如果没有获胜者,因为所有冲突的重载都提供无法从其他重载获得的唯一功能,那么如何?

runtimeclass Widget
{
    static Widget Create(Uri source);
    static Widget Create(IInputStream source);
}

假设 组件对象可以从 Uri 创建,也可以从输入流创建。 如果将一个重载标记为默认重载,则动态类型的编程语言将完全失去对另一种语言的访问权限。

若要解决此问题,请为两个版本提供不同的名称,以便它们不会重载。

runtimeclass Widget
{
    static Widget CreateFromUri(Uri source);
    static Widget CreateFromStream(IInputStream source);
}

现在,这两种创建模式都可用于所有语言。

另请参阅方法重载和基于类的投影

属性[interface_name]

特性 interface_name 指定包含 类的实例成员的接口的名称和 IID。 默认情况下,编译器使用与方法相同的唯一编号算法来分配接口 名称

在下面的示例中 interface_name ,应用于 runtimeclass 的 特性指定接口的名称和 IID,该接口包含类的所有实例成员,这些实例成员未以其他方式分配给接口。 因此 ,LineHeightLineStackingStrategyMarginTextAlignmentIBlock 接口 的成员。

但是 ,HorizontalTextAlignmentIBlock2interface_name 接口的成员,因为包含该成员的属性。

[interface_name("Windows.UI.Xaml.Documents.IBlock", 4bce0016-dd47-4350-8cb0-e171600ac896)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    Double LineHeight;
    Windows.UI.Xaml.LineStackingStrategy LineStackingStrategy;
    Windows.UI.Xaml.Thickness Margin;
    Windows.UI.Xaml.TextAlignment TextAlignment;

    [interface_name("Windows.UI.Xaml.Documents.IBlock2", 5ec7bdf3-1333-4a92-8318-6caedc12ef89)]
    {
        Windows.UI.Xaml.TextAlignment HorizontalTextAlignment;
    }
    ...
}

还可使用 属性 interface_name 强制生成接口。 在下面的示例中,StateTriggerBase 不需要 IStateTriggerBaseinterface_name,只是存在导致生成它的 属性。

[interface_name("Windows.UI.Xaml.IStateTriggerBase", 48b20698-af06-466c-8052-93666dde0e49)]
unsealed runtimeclass StateTriggerBase
{
    protected void SetActive(Boolean IsActive);
};

因此,当类说明不引用接口,但需要接口来实现类时,MIDL 3.0 编译器会根据需要合成并添加接口。

如果不必要地 default_interface 使用 ,则 MIDL 3.0 会生成一个额外的 接口,并设置为默认值。

属性[method_name]

每个Windows 运行时接口都有一个等效的应用程序二进制接口 (ABI) 接口。 ABI 接口要求所有成员都有唯一的名称。 MIDL 3.0 有两种情况:成员没有名称或没有唯一名称。

  • 构造函数, 和
  • 两个或多个重载方法。

在这些情况下,MIDL 3.0 编译器将在必要时合成唯一的成员名称。

默认情况下,编译器为 < ABI 接口中的等效方法分配名称 className、className2><><、className3> 等构造函数方法。 换句话说,添加最小的未使用整数数字后缀 (从 2 开始) ,使构造函数方法名称唯一。

同样,对于重载方法,对于一系列重载的第一个方法 (按词法顺序) ,编译器将原始方法名称用于等效的 ABI 接口方法。 后续重载通过向原始名称添加最小的未使用整数数字后缀 (从 2) 。

例如,以下 IDL 声明了 DoWork 的三个重载,以及另一个名为 DoWork3 的方法的两个重载。

void DoWork(Int32 x);
void DoWork3(Int32 x);
void DoWork(Int32 x, Int32 y);
void DoWork(Int32 x, Int32 y, Int32 z);
void DoWork3(Int32 x, Int32 y);

默认情况下 (名称 DoWork3 已被) ,因此编译器会向 DoWork 的三个重载提供名称

  • DoWork
  • DoWork2
  • DoWork4

DoWork3不是DoWork 重载。 默认情况下,编译器向 DoWork3 的两个重载提供名称

  • DoWork3
  • DoWork32

然后,函数将按 vtable 顺序显示为

  • DoWork
  • DoWork3
  • DoWork2
  • DoWork4
  • DoWork32

可以使用 属性重写编译器的默认名称 method_name 分配。

在下一个示例中,将指示编译器使用默认构造函数成员的名称CreateInstance

unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    [method_name("CreateInstance")] protected Block();
    ...
}

[static_name]特性

static_name特性指定包含类的静态成员的接口的名称和 IID。

在下一个示例中, static_name 应用于 runtimeclass 的特性指定了接口的名称和 IID,该接口包含未分配给某个接口的类的所有静态成员。 因此, LineHeightPropertyLineStackingStrategyPropertyMarginPropertyTextAlignmentPropertyIBlockStatics 接口的成员。

但是, HorizontalTextAlignmentPropertyIBlockStatics2 接口的成员,因为 static_name 属性包含该成员。

[static_name("Windows.UI.Xaml.Documents.IBlockStatics", f86a8c34-8d18-4c53-aebd-91e610a5e010)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    static Windows.UI.Xaml.DependencyProperty LineHeightProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty LineStackingStrategyProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty MarginProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty TextAlignmentProperty{ get; };

    [static_name("Windows.UI.Xaml.Documents.IBlockStatics2", af01a4d6-03e3-4cee-9b02-2bfc308b27a9)]
    {
        static Windows.UI.Xaml.DependencyProperty HorizontalTextAlignmentProperty{ get; };
    }
    ...
}