共用方式為


從 C# 移到 C++/WinRT

提示

如果您先前已閱讀過本主題,並且因為想了解特定工作而返回此處,您可以跳至<根據正在執行的工作來尋找內容>一節。

本主題詳細說明將 C# 專案中的原始程式碼移植到其在 C++/WinRT 中的對等項目時所涉及的技術詳細資料。

如需移植其中一個通用 Windows 平台 (UWP) 應用程式範例的案例研究,請參閱附屬主題:將剪貼簿範例從 C# 移植到 C++/WinRT。 您可以遵循逐步解說來獲得移植的方法和體驗,然後再自行移植範例。

如何準備,以及預期的事項

將剪貼簿範例從 C# 移植到 C++/WinRT 的案例研究會說明將專案移植到 C++/WinRT 時所需的各種軟體設計決策範例。 因此,您應先了解現有程式碼的運作方式,再準備進行移植。 如此一來,您就可以大致了解應用程式的功能,以及程式碼的結構,然後您所做的決定將會讓您向前邁進,並以正確的方向進行。

就預期的移植變更種類而言,您可以將其分為四個類別。

  • 移植語言投影。 Windows 執行階段 (WinRT) 會「投影」到各種程式設計語言中。 每一個語言投影都會設計成所述程式設計語言慣用的型態。 針對 C#,某些 Windows 執行階段類型會投影為 .NET 類型。 例如,您會將 System.Collections.Generic.IReadOnlyList<T> 轉譯回 Windows.Foundation.Collections.IVectorView<T>。 此外,在 C# 中,某些 Windows 執行階段作業會投影為方便的 C# 語言功能。 例如,您會在 C# 使用 += 運算子語法來註冊事件處理委派。 因此,您會把這類語言功能轉譯回要執行的基本作業 (在此範例中為事件註冊)。
  • 移植語言語法。 這些變更有許多都是簡單的機械轉換,也就是將符號取代為另一個符號。 例如,將點 (.) 變更為雙冒號 (::)。
  • 移植語言程序。 這其中有一些是簡單的重複變更 (例如將 myObject.MyProperty 變更為 myObject.MyProperty())。 而其他則是更深入的變更 (例如,將牽涉到使用 System.Text.StringBuilder 的程序移植到涉及使用 std::wostringstream 的程序)。
  • C++/WinRT 特定的移植相關工作。 Windows 執行階段的特定詳細資料會透過 C# 在幕後進行隱含處理。 這些詳細資料會在 C++/WinRT 中明確執行。 例如,您可以使用 .idl 檔案來定義您的執行階段類別。

在接下來的工作型索引之後,本主題中的其餘章節會根據上述分類進行結構化。

根據正在執行的工作來尋找內容

Task Content
撰寫 Windows 執行階段元件 (WRC) 某些功能 (或某些 API 呼叫) 只能使用 C++ 來達成。 您可以將該功能納入 C++/WinRT WRC,然後從 C# 應用程式之類的項目中取用 WRC。 請參閱使用 C++/WinRT 的 Windows 執行階段元件如果您正在 Windows 執行階段元件中撰寫執行階段類別
移植非同步方法 建議您將 C++/WinRT 執行階段類別中非同步方法的第一行設定為 auto lifetime = get_strong(); (請參閱安全地存取類別成員協同程式中的 this 指標)。

Task 移植,請參閱非同步動作
Task<T> 移植,請參閱非同步作業
async void 移植,請參閱射後不理 (Fire-and-Forget) 方法
移植類別 首先,決定類別是否必須是執行階段類別,或是否可以是一般類別。 為協助您做決定,請參閱使用 C++/WinRT 撰寫 API 的最初部分。 接下來,請參閱下面三個資料列。
移植執行階段類別 在 C++ 應用程式外部共用功能的類別,或用於 XAML 資料繫結中的類別。 請參閱如果您正在 Windows 執行階段元件中撰寫執行階段類別,或是如果您正在撰寫要在 XAML UI 中參考的執行階段類別

這些連結會更詳細地說明這一點,但必須在 IDL 中宣告執行階段類別。 如果您的專案已經包含 IDL 檔案 (例如 Project.idl),則建議您在該檔案中宣告任何新的執行階段類別。 在 IDL 中,宣告將在您應用程式外部使用或將在 XAML 中使用的任何方法和資料成員。 更新 IDL 檔案之後,在您專案的 Generated Files 資料夾重建並查看產生的 stub 檔案 (.h.cpp) (在已選取專案節點的方案總管中,確定 [顯示所有檔案] 已切換為開啟)。 比較 stub 檔案與專案中已經存在的檔案,並視需要新增檔案或新增/更新函式簽章。 stub 檔案語法一律是正確的,因此我們建議您使用此檔案來將組建錯誤降至最低。 一旦專案中的 stub 與 stub 檔案中的項目相符,您就可以藉由移植 C# 程式碼來繼續並加以實作。
移植一般類別 請參閱如果您「不」撰寫執行階段類別
撰寫 IDL Microsoft 介面定義語言 3.0 的簡介
如果您正在撰寫要在 XAML UI 中參考的執行階段類別
取用 XAML 標記中的物件
定義 IDL 中的執行階段類別
移植集合 使用 C++/WinRT 的集合
讓資料來源可供 XAML 標記使用
關聯容器
向量成員存取
移植事件 事件處理常式委派為類別成員
撤銷事件處理常式委派
移植方法 從 C#:private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
至 C++/WinRT .h 檔案:fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
至 C++/WinRT .cpp 檔案:fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
移植字串 C++/WinRT 中的字串處理
ToString
字串建立
進行字串的 Box 處理和 Unbox 處理
類型轉換 (類型轉型) C#: o.ToString()
C++/WinRT:to_hstring(static_cast<int>(o))
另請參閱 ToString

C#: (Value)o
C++/WinRT:unbox_value<Value>(o)
unboxing 失敗時擲回。 另請參閱 Boxing 與 unboxing

C#: o as Value? ?? fallback
C++/WinRT:unbox_value_or<Value>(o, fallback)
unboxing 失敗時傳回後援。 另請參閱 Boxing 與 unboxing

C#: (Class)o
C++/WinRT:o.as<Class>()
轉換失敗時擲回。

C#: o as Class
C++/WinRT:o.try_as<Class>()
如果轉換失敗,則傳回 null。

涉及語言投射的變更

類別 C# C++/WinRT 另請參閱
非類型化物件 objectSystem.Object Windows::Foundation::IInspectable 移植 EnableClipboardContentChangedNotifications 方法
投影命名空間 using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
集合的大小 collection.Count collection.Size() 移植 BuildClipboardFormatsOutputString 方法
典型集合類型 IList<T>,然後 [新增] 以新增元素。 IVector<T>,然後 [附加] 以新增元素。 如果您使用 std::vector 任何位置,則 push_back 以新增元素。
唯讀集合類型 IReadOnlyList<T> IVectorView<T> 移植 BuildClipboardFormatsOutputString 方法
事件處理常式委派為類別成員 myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); 移植 EnableClipboardContentChangedNotifications 方法
撤銷事件處理常式委派 myObject.EventName -= Handler; myObject.EventName(token); 移植 EnableClipboardContentChangedNotifications 方法
關聯容器 IDictionary<K, V> IMap<K, V>
向量成員存取 x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

註冊/撤銷事件處理常式

在 C++/WinRT 中,您有數個語法選項可供您註冊/撤銷事件處理常式委派,如藉由在 C++/WinRT 使用委派來處理事件中所述。 另請參閱移植 EnableClipboardContentChangedNotifications 方法.

例如,有時候事件收件者 (處理事件的物件) 即將終結時,您可以撤銷事件處理常式,讓事件來源 (引發事件的物件) 不會呼叫已終結的物件。 請參閱撤銷已註冊的委派。 在這種情況下,請為事件處理常式建立 event_token 成員變數。 如需範例,請參閱移植 EnableClipboardContentChangedNotifications 方法

您也可以在 XAML 標記中註冊事件處理常式。

<Button x:Name="OpenButton" Click="OpenButton_Click" />

在 C# 中,OpenButton_Click 方法可以是私有的,而且 XAML 仍可將其連線到 OpenButton 所引發的 ButtonBase.Click 事件。

在 C++/WinRT 中,「如果您想要在 XAML 標記中註冊 OpenButton_Click 方法」,則該方法在實作類型中必須是公用的。 如果您只在命令式程式碼中註冊事件處理常式,則事件處理常式不需要是公用的。

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
        void OpenButton_Click(
            winrt::Windows:Foundation::IInspectable const& sender,
            winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
    }
};

或者,您可以讓註冊 XAML 頁面成為實作類型的同伴,並且讓 OpenButton_Click 成為私有的。

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
    private:
        friend MyPageT;
        void OpenButton_Click(
            winrt::Windows:Foundation::IInspectable const& sender,
            winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
    }
};

最後一個案例是您要移植的 C# 專案從標記繫結到事件處理常式 (如需更多案例背景,請參閱 Functions in x:Bind)。

<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />

您可以將該標記變更為更簡單的 Click="OpenButton_Click"。 或者,如果想要的話,也可以保留該標記不變。 為了加以支援,您只需要在 IDL 中宣告事件處理常式。

void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);

注意

將函式宣告為 void (即使您實作「射後不理」(Fire and Forget))。

涉及語言語法的變更

類別 C# C++/WinRT 另請參閱
存取修飾詞 public \<member\> public:
    \<member\>
移植 Button_Click 方法
存取資料成員 this.variable this->variable  
非同步動作 async Task ... IAsyncAction ... IAsyncAction 介面使用 C++/WinRT 的並行和非同步作業
非同步作業 async Task<T> ... IAsyncOperation<T> ... IAsyncOperation 介面使用 C++/WinRT 的並行和非同步作業
「射後不理」 (Fire-and-forget) 方法 (暗指非同步) async void ... winrt::fire_and_forget ... 移植 CopyButton_Click 方法射後不理 (Fire-and-forget)
存取列舉常數 E.Value E::Value 移植 DisplayChangedFormats 方法
合作等待 await ... co_await ... 移植 CopyButton_Click 方法
作為私人欄位的預測類型集合 private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID 結構 private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
命名空間分隔符號 A.B.T A::B::T
Null null nullptr 移植 UpdateStatus 方法
取得類型物件 typeof(MyType) winrt::xaml_typename<MyType>() 移植 Scenarios 屬性
方法的參數宣告 MyType MyType const& 參數傳遞
非同步方法的參數宣告 MyType MyType 參數傳遞
呼叫靜態方法 T.Method() T::Method()
字串 stringSystem.String winrt::hstring C++/WinRT 中的字串處理
字串常值 "a string literal" L"a string literal" 移植建構函式 CurrentFEATURE_NAME
推斷 (或推算) 的類型 var auto 移植 BuildClipboardFormatsOutputString 方法
使用指示詞 using A.B.C; using namespace A::B::C; 移植建構函式 CurrentFEATURE_NAME
逐字/原始字串常值 @"verbatim string literal" LR"(raw string literal)" 移植 DisplayToast 方法

注意

如果標頭檔不包含指定命名空間的 using namespace 指示詞,則您必須完整限定該命名空間的所有類型名稱;或者至少要限定在足以讓編譯器找到這些名稱的範圍。 如需範例,請參閱移植 DisplayToast 方法

移植類別與成員

針對每個 C# 類型,您都必須決定要將其移植到 Windows 執行階段類型,還是一般 C++ 類別/結構/列舉。 如需詳細資訊,以及說明如何做出這些決策的詳細範例,請參閱將剪貼簿範例從 C# 移植到 C++/WinRT

C# 屬性通常會成為存取子函式、更動子函式和支援資料成員。 如需詳細資訊和範例,請參閱移植 IsClipboardContentChangedEnabled 屬性

對於非靜態欄位,請將其設為 實作類型的資料成員。

C# 靜態欄位會成為 C++/WinRT 靜態存取子和 (或) 更動子涵式。 如需詳細資訊和範例,請參閱移植建構函式 CurrentFEATURE_NAME

同樣地,對於成員函式,您必須決定每個成員函式是否屬於 IDL,或是否為實作類型的公用或私有成員函式。 如需詳細資訊,以及如何決定的範例,請參閱 MainPage 類型的 IDL

移植 XAML 標記和資產檔案

將剪貼簿範例從 C# 移植到 C++/WinRT 案例中,我們可以在 C# 和 C++/WinRT 專案中使用「相同」XAML 標記 (包括資源) 和資產檔案。 在某些情況下,您需要編輯標記來達到此目的。 請參閱複製完成移植 MainPage 所需的 XAML 和樣式

涉及語言內程序的變更

類別 C# C++/WinRT 另請參閱
非同步方法中的存留期管理 N/A auto lifetime{ get_strong() };
auto lifetime = get_strong();
移植 CopyButton_Click 方法
處置 using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
移植 CopyImage 方法
建構物件 new MyType(args) MyType{ args }
MyType(args)
移植 Scenarios 屬性
建立未初始化的參考 MyType myObject; MyType myObject{ nullptr };
MyType myObject = nullptr;
移植建構函式 CurrentFEATURE_NAME
使用引述將物件建構到變數中 var myObject = new MyType(args); auto myObject{ MyType{ args } };
auto myObject{ MyType(args) };
auto myObject = MyType{ args };
auto myObject = MyType(args);
MyType myObject{ args };
MyType myObject(args);
移植 Footer_Click 方法
在無需引數的情況下,將物件建構到變數中 var myObject = new T(); MyType myObject; 移植 BuildClipboardFormatsOutputString 方法
物件初始化速記 var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
大量向量作業 var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
移植 CopyButton_Click 方法
反復查看集合 foreach (var v in c) for (auto&& v : c) 移植 BuildClipboardFormatsOutputString 方法
攔截例外狀況 catch (Exception ex) catch (winrt::hresult_error const& ex) 移植 PasteButton_Click 方法
例外狀況詳細資料 ex.Message ex.message() 移植 PasteButton_Click 方法
取得屬性值 myObject.MyProperty myObject.MyProperty() 移植 NotifyUser 方法
設定屬性值 myObject.MyProperty = value; myObject.MyProperty(value);
遞增屬性值 myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
針對字串,請切換至產生器
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Windows 執行階段字串的語言字串 N/A winrt::hstring{ s }
字串建立 StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
字串建立
字串插補 $"{i++}) {s.Title}" winrt::to_hstring 和 (或) winrt::hstring::operator+ 移植 OnNavigatedTo 方法
用於比較的空字串 System.String.Empty winrt::hstring::empty 移植 UpdateStatus 方法
建立空白字串 var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
字典作業 map[k] = v; // replaces any existing
v = map[k]; // throws if not present
map.ContainsKey(k)
map.Insert(k, v); // replaces any existing
v = map.Lookup(k); // throws if not present
map.HasKey(k)
類型轉換 (失敗時擲回) (MyType)v v.as<MyType>() 移植 Footer_Click 方法
類型轉換 (失敗時為 null) v as MyType v.try_as<MyType>() 移植 PasteButton_Click 方法
具有 x:Name 的 XAML 元素是屬性 MyNamedElement MyNamedElement() 移植建構函式 CurrentFEATURE_NAME
切換至 UI 執行緒 CoreDispatcher.RunAsync CoreDispatcher.RunAsyncwinrt::resume_foreground 移植 NotifyUser 方法,以及移植 HistoryAndRoaming 方法
XAML 頁面中命令式程式碼的 UI 元素結構 請參閱 UI 元素結構 請參閱 UI 元素結構

下列各節將詳細說明資料表中的某些項目。

UI 元素建構

這些程式碼範例示範 XAML 頁面的命令式程式碼中的 UI 元素結構。

var myTextBlock = new TextBlock()
{
    Text = "Text",
    Style = (Windows.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
    winrt::unbox_value<Windows::UI::Xaml::Style>(
        Resources().Lookup(
            winrt::box_value(L"MyTextBlockStyle")
        )
    )
);

ToString()

C# 類型會提供 Object.ToString 方法。

int i = 2;
var s = i.ToString(); // s is a System.String with value "2".

C++/WinRT 不會直接提供此功能,但您可以轉向使用替代方案。

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT 也針對有限的類型數量支援 winrt::to_hstring。 您必須為想要字串化的任何其他類型新增多載。

語言 將 int 字串化 將列舉字串化
C# string result = "hello, " + intValue.ToString();
string result = $"hello, {intValue}";
string result = "status: " + status.ToString();
string result = $"status: {status}";
C++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

在將列舉字串化的情況下,您需要提供 winrt::to_hstring 的實作。

namespace winrt
{
    hstring to_hstring(StatusEnum status)
    {
        switch (status)
        {
        case StatusEnum::Success: return L"Success";
        case StatusEnum::AccessDenied: return L"AccessDenied";
        case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
        default: return to_hstring(static_cast<int>(status));
        }
    }
}

資料繫結通常會隱含地使用這些字串化作業。

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

這些繫結會執行所繫結屬性的 winrt::to_hstring。 在第二個範例 (StatusEnum) 的情況下,您必須提供自己的 winrt::to_hstring 多載,否則會收到編譯器錯誤。

另請參閱移植 Footer_Click 方法

字串建立

針對字串建立,C# 有內建的 StringBuilder 類型。

類別 C# C++/WinRT
字串建立 StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
附加 Windows 執行階段字串,保留 null builder.Append(s); builder << std::wstring_view{ s };
加入新行 builder.Append(Environment.NewLine); builder << std::endl;
存取結果 s = builder.ToString(); ws = builder.str();

另請參閱移植 BuildClipboardFormatsOutputString 方法,以及移植 DisplayChangedFormats 方法

在主要 UI 執行緒上執行程式碼

此範例取自條碼掃描器範例

當您想要在 C# 專案的主要 UI 執行緒上執行作業時,通常會使用 CoreDispatcher.RunAsync 方法,如下所示。

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Do work on the main UI thread here.
    });
}

使用 C++/WinRT 來表達更為容易。 請注意,我們接受參數值是假設我們想要在第一個暫止點 (此案例中為 co_await) 之後存取它們。 如需詳細資訊,請參閱參數傳遞

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await Dispatcher();
    // Do work on the main UI thread here.
}

如果您需要以預設值以外的優先順序來執行工作,請參閱 winrt::resume_foreground 函式,其有採用 priority 的多載。 如需示範如何等候對 winrt::resume_foreground 呼叫的程式碼範例,請參閱考量使用執行緒親和性程式設計

定義 IDL 中的執行階段類別

請參閱 MainPage 類型的 IDL合併 .idl 檔案

包含您需要的 C++/WinRT Windows 命名空間標頭檔案

在 C++/WinRT 中,每當您要使用 Windows 命名空間的類型時,您必須包含對應的 C++/WinRT Windows 命名空間標頭檔案。 如需範例,請參閱移植 NotifyUser 方法

Box 處理和 Unbox 處理

C# 會自動將純量 Box 處理為物件。 C++/WinRT 會要求您明確地呼叫 winrt::box_value 函式。 這兩種語言都需要您明確地進行 Unbox 處理。 請參閱使用 C++/WinRT 進行 Box 處理和 Unbox 處理

在後續表格中,我們將使用下列定義。

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
作業 C# C++/WinRT
Box 處理 o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Unbox 處理 i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

如果您嘗試將 null 指標 Unbox 處理為某個實值類型,則 C++/CX 和 C# 會引發例外狀況。 C++/WinRT 會將此視為程式設計錯誤,並且毀損。 在 C++/WinRT 中,如果您想處理物件不是您所認為類型的情況,請使用 winrt::unbox_value_or 函式。

案例 C# C++/WinRT
進行已知整數的 Unbox 處理 i = (int)o; i = unbox_value<int>(o);
如果 o 為 null System.NullReferenceException 毀損
如果 o 不是已 Box 處理的 int System.InvalidCastException 毀損
進行 int 的 Unbox 處理,若為 null 則使用遞補;若為其他任何項目則會毀損 i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
可能的話,進行 int 的 Unbox 處理;其他任何項目使用遞補 i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

如需範例,請參閱移植 OnNavigatedTo 方法,以及移植 Footer_Click 方法

進行字串的 Box 處理和 Unbox 處理

字串在某些方面是實值類型,而在其他方面則是參考類型。 C# 和 C++/WinRT 會以不同的方式處理字串。

ABI 類型 HSTRING 是參考計數字串的指標。 但是它並非衍生自 IInspectable,因此在技術上並不是「物件」。 此外, null HSTRING 代表空字串。 將非衍生自 IInspectable 的項目包裝在 IReference<T>內,即可完成 Box 處理,而 Windows 執行階段會以 PropertyValue 物件形式提供標準實作 (自訂類型會回報為 PropertyType::OtherType)。

C# 表示作為參考類型的 Windows 執行階段字串;而 C++/WinRT 會將字串投影為實值類型。 這表示已進行 Box 處理的 null 字串可以有不同的表示法 (取決於您達成的方式)。

行為 C# C++/WinRT
宣告 object o;
string s;
IInspectable o;
hstring s;
字串類型類別 參考類型 值類型
null HSTRING 投影為 "" hstring{}
Null 和 "" 相同嗎? No Yes
Null 的有效性 s = null;
s.Length 引發 NullReferenceException
s = hstring{};
s.size() == 0 (有效)
如果將 Null 字串指派給物件 o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
如果將 "" 指派給物件 o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

基本 Box 處理和 Unbox 處理。

作業 C# C++/WinRT
進行字串的 Box 處理 o = s;
空字串會變成非 Null 物件。
o = box_value(s);
空字串會變成非 Null 物件。
進行已知字串的 Unbox 處理 s = (string)o;
Null 物件會變成 Null 字串。
InvalidCastException (如果不是字串)。
s = unbox_value<hstring>(o);
Null 物件損毀。
如果不是字串,則會損毀。
將可能的字串進行 Unbox 處理 s = o as string;
Null 物件或非字串會變成 Null 字串。

OR

s = o as string ?? fallback;
Null 或非字串會變成遞補。
保留空字串。
s = unbox_value_or<hstring>(o, fallback);
Null 或非字串會變成遞補。
保留空字串。

讓類別可供 {Binding} 標記延伸使用

如果您想要使用 {binding} 標記延伸將資料繫結至您的資料類型,請參閱使用 {Binding} 宣告的繫結物件

取用 XAML 標記中的物件

在 C# 專案中,您可以使用來自 XAML 標記的私有成員和具名元素。 但是在 C++/WinRT 中,使用 XAML {x:Bind} 標記延伸 取用的所有實體都必須公開於 IDL 中。

此外,布林值的繫結會在 C# 中顯示 truefalse,但是在 C++/WinRT 中顯示 Windows.Foundation.IReference`1<Boolean>

如需詳細資訊和程式碼範例,請參閱使用標記中的物件

讓資料來源可供 XAML 標記使用

在 C++/WinRT 2.0.190530.8 版或更新版本中,winrt::single_threaded_observable_vector 會建立可觀察的向量,其同時支援 IObservableVector<T>IObservableVector<IInspectable>。 如需範例,請參閱移植 Scenarios 屬性

您可以撰寫如下所示的 Midl 檔案 (.idl) (另請參閱將執行階段類別分解成 Midl 檔檔案 (.idl))。

namespace Bookstore
{
    runtimeclass BookSku { ... }

    runtimeclass BookstoreViewModel
    {
        Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
    }

    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

其實作方式如下所示。

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
	Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...

如需詳細資訊,請參閱 XAML 項目控制項;繫結至 C++/WinRT 集合使用 C++/WinRT 的集合

讓資料來源可供 XAML 標記使用 (在 C++/WinRT 2.0.190530.8 之前)

XAML 資料繫結要求項目來源實作 IIterable<IInspectable>,以及下列其中一個介面組合。

  • IObservableVector<IInspectable>
  • IBindableVectorINotifyCollectionChanged
  • IBindableVectorIBindableObservableVector
  • IBindableVector 本身 (不會回應變更)
  • IVector<IInspectable>
  • IBindableIterable (會逐一查看元素並儲存至私用集合)

在執行階段無法偵測 IVector<T> 等一般介面。 每個 IVector<T> 都有不同的介面識別碼 (IID),這是 T的函數。任何開發人員都可以任意擴充 T 集合,所以顯然 XAML 繫結程式碼永遠不會知道要查詢的完整集合。 該限制不是 C# 的問題,因為每個實作 IEnumerable<T> 的 CLR 物件都會實作 IEnumerable。 在 ABI 層級,這表示每個實作 IObservableVector<T> 的物件都會自動實作 IObservableVector<IInspectable>

C++/WinRT 不提供該保證。 如果 C++/WinRT 執行階段類別會實作 IObservableVector<T>,我們無法假設也會提供 IObservableVector<IInspectable> 的實作。

因此,前一個範例需要如下所示。

...
runtimeclass BookstoreViewModel
{
    // This is really an observable vector of BookSku.
    Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}

其實作方式。

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
    // This is really an observable vector of BookSku.
	Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...

如果您需要存取 m_bookSkus 中的物件,則必須將其 QI 回到 Bookstore::BookSku

Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
    for (auto&& obj : m_bookSkus)
    {
        auto bookSku = obj.as<Bookstore::BookSku>();
        if (bookSku.Title() == title) return bookSku;
    }
    return nullptr;
}

衍生類別

為了從執行階段類別衍生,基底類別必須「可組合」。 C# 不要求您採取任何特殊步驟,即可讓類別變為可組合,而 C++/WinRT 則會要求您採取步驟。 您可使用未密封的關鍵字,指出您希望類別可作為基底類別使用。

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

實作類型的標頭檔案中,您必須先包含基底類別標頭檔,才可包含衍生類別的自動產生標頭。 否則,您會收到錯誤,例如「此類型當作運算式使用並不合法」。

// DerivedPage.h
#include "BasePage.h"       // This comes first.
#include "DerivedPage.g.h"  // Otherwise this header file will produce an error.

namespace winrt::MyNamespace::implementation
{
    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        ...
    }
}

重要 API