使用 C++/WinRT 将值装箱为 IInspectable 并从中拆箱

注释

使用 winrt::box_valuewinrt::unbox_value 函数,不仅可以对标量值进行装箱和拆箱,还可以对大多数数组类型进行装箱和拆箱(枚举数组除外)。 只能使用 winrt::unbox_value_or 函数对标量值取消装箱。

IInspectable 接口是Windows 运行时(WinRT)中每个运行时类的根接口。 这是一个类似的概念:IUnknown 处于每个 COM 接口和类的根部;而 System.Object 则处于每个 通用类型系统 类的根部。

换句话说,期望 IInspectable 的函数可以传递任何运行时类的实例。 但是,不能将标量值(如数字或文本值)或数组直接传递给此类函数。 相反,需要将标量或数组值包装在引用类对象内。 该封装过程称为将该值装箱

Important

你可以对任何可传递给 Windows 运行时 API 的类型进行装箱和拆箱。 换句话说,Windows 运行时 类型。 数值和文本值(字符串)和数组是上面给出的一些示例。 另一个 struct 示例是在 IDL 中定义的。 如果你试图对一个常规的 C++ struct(即未在 IDL 中定义的那种)进行装箱,那么编译器会提醒你,你只能对 Windows 运行时 类型进行装箱。 运行时类是一种Windows 运行时类型,但当然可以将运行时类传递给Windows 运行时 API,而无需将它们装箱。

C++/WinRT 提供 winrt::box_value 函数,该函数接受标量值或数组值,并返回装箱为 IInspectable 的值。 若要将 IInspectable 解压缩回标量或数组值,请使用 winrt::unbox_value 函数。 若要将 IInspectable 解压缩回标量值,还有 winrt::unbox_value_or 函数。

装箱值的示例

LaunchActivatedEventArgs::Arguments 访问器函数返回 winrt::hstring,这是标量值。 我们可以将该 hstring 值装箱,并将其传递给接受 IInspectable 的函数,如下所示。

void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
    ...
    rootFrame.Navigate(winrt::xaml_typename<BlankApp1::MainPage>(), winrt::box_value(e.Arguments()));
    ...
}

若要设置 XAML 按钮的内容属性,请调用 Button::Content mutator 函数。 若要将内容属性设置为字符串值,可以使用此代码。

Button().Content(winrt::box_value(L"Clicked"));

首先, hstring 转换构造函数将字符串文本转换为 hstring。 随后会调用接受 hstringwinrt::box_value 重载版本。

IInspectable 拆箱示例

在需要 IInspectable 的自己的函数中,可以使用 winrt::unbox_value 取消装箱,并且可以使用 winrt::unbox_value_or 取消装箱,并具有默认值。 还可以使用 try_as 取消装箱到 std::optional

void Unbox(winrt::Windows::Foundation::IInspectable const& object)
{
    hstring hstringValue = unbox_value<hstring>(object); // Throws if object is not a boxed string.
    hstringValue = unbox_value_or<hstring>(object, L"Default"); // Returns L"Default" if object is not a boxed string.
    float floatValue = unbox_value_or<float>(object, 0.f); // Returns 0.0 if object is not a boxed float.
    std::optional<int> optionalInt = object.try_as<int>(); // Returns std::nullopt if object is not a boxed int.
}

确定已装箱值的类型

如果收到一个装箱值,并且不确定它包含的是哪种类型(因为需要知道其类型才能拆箱),则可以查询该装箱值的 IPropertyValue 接口,然后调用其 Type。 下面是一个代码示例。

WINRT_ASSERT 是宏定义,它扩展到 _ASSERTE

float pi = 3.14f;
auto piInspectable = winrt::box_value(pi);
auto piPropertyValue = piInspectable.as<winrt::Windows::Foundation::IPropertyValue>();
WINRT_ASSERT(piPropertyValue.Type() == winrt::Windows::Foundation::PropertyType::Single);

重要 API