Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Подсказка
Если вы прочитали этот раздел раньше, и вы вернелись к нему с определенной задачей, то вы можете перейти к содержимому поиска на основе задачи, которую вы выполняете в этом разделе.
В этом разделе подробно описаны технические сведения, связанные с переносом исходного кода в проекте C# в эквивалент C++/WinRT.
Пример переноса одного из примеров приложений Universal Windows Platform (UWP) см. в сопутствующем разделе Перенос примера буфера обмена с C# на C++/WinRT. Вы можете получить практику и опыт по переносу, следуя этому пошаговому руководству и самостоятельно перенося пример по ходу дела.
Как подготовиться и что ожидать
Пример переноса примера буфера обмена на C++/WinRT из C# иллюстрирует примеры типов решений по проектированию программного обеспечения, которые вы будете принимать при переносе проекта на 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файла для определения классов среды выполнения.
После следующего индекса на основе задач остальные разделы в этом разделе структурированы в соответствии с таксономией выше.
Поиск содержимого на основе выполняемой задачи
| задачи | Content |
|---|---|
| Создание компонента среда выполнения Windows (WRC) | Некоторые функциональные возможности можно достичь (или некоторые вызовы API) только с помощью C++. Эту функцию можно учитывать в WRC C++/WinRT, а затем использовать WRC из (например) приложения C#. См. Компоненты среда выполнения Windows с C++/WinRT и Создание класса времени выполнения в компоненте среда выполнения Windows. |
| Перенос асинхронного метода | Рекомендуется, чтобы первой строкой асинхронного метода в классе среды выполнения C++/WinRT было auto lifetime = get_strong(); (см. Безопасный доступ к указателю this в корутине-члене класса).Перенос с Task, см. Асинхронное действие.Перенос с Task<T>, см. Асинхронная операция.При переносе из async void см. метод «отправил и забыл». |
| Перенос класса | Сначала определите, должен ли класс быть классом среды выполнения или может ли он быть обычным классом. Чтобы помочь вам принять решение, см. самое начало статьи Создание API с помощью C++/WinRT. Затем см. следующие три строки ниже. |
| Перенос класса среды выполнения | Класс, который использует функциональные возможности за пределами приложения C++ или класса, используемого в привязке данных XAML. См. статью "Если вы создаете класс среды выполнения в компоненте среда выполнения Windows" или "Если вы создаете класс среды выполнения для ссылки в пользовательском интерфейсе XAML". Эти ссылки подробно описывают это, но класс среды выполнения должен быть объявлен в IDL. Если project уже содержит IDL-файл (например, Project.idl), рекомендуется объявить любой новый класс среды выполнения в этом файле. В IDL объявите все методы и элементы данных, которые будут использоваться вне приложения или будут использоваться в XAML. После обновления IDL-файла перестройте проект и просмотрите сгенерированные файлы-заглушки (.h и .cpp) в папке Generated Files вашего проекта (в окне Обозреватель решений, выбрав узел проекта, убедитесь, что включен параметр Show All Files). Сравните stub-файлы с файлами, уже имеющимися в вашем проекте, и при необходимости добавьте недостающие файлы или добавьте либо обновите сигнатуры функций. Синтаксис файла-заглушки всегда корректен, поэтому мы рекомендуем использовать его, чтобы свести к минимуму ошибки сборки. Как только заглушки в вашем проекте совпадут с заглушками в файлах заглушек, можно переходить к их реализации, перенеся код C#. |
| Перенести обычный класс | См. статью "Если вы не создаете класс среды выполнения". |
| Автор IDL |
Общие сведения о языке определения интерфейса (Майкрософт) 3.0 Если вы создаете класс среды выполнения, на который можно ссылаться в пользовательском интерфейсе XAML Использование объектов из разметки XAML Определение классов среды выполнения в IDL |
| Перенос коллекции |
Коллекции с помощью C++/WinRT Предоставление доступа к источнику данных в разметке XAML Ассоциативный контейнер Доступ к элементу вектора |
| Перенос события |
Делегат обработчика событий как член класса Отзыв делегата обработчика событий |
| Перенести метод | Из C#: private async void SampleButton_Tapped(object sender, Microsoft.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 Строковое построение Упаковка и распаковка строки |
| Преобразование типов (приведение типов) | C#: o.ToString()C++/WinRT: to_hstring(static_cast<int>(o))См. также статью ToString. C#: (Value)oC++/WinRT: unbox_value<Value>(o)Вызывается при сбое распаковки. Также см. Упаковка и распаковка. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Возвращает резервный вариант, если распаковка завершается ошибкой. См. также Упаковка и распаковка. C#: (Class)oC++/WinRT: o.as<Class>()Вызывается, если преобразование завершается ошибкой. C#: o as ClassC++/WinRT: o.try_as<Class>()Возвращает значение NULL, если преобразование завершается ошибкой. |
Изменения, связанные с проекцией языка
| Category | C# | C++/WinRT | См. также |
|---|---|---|---|
| Нетипизированный объект |
object, или System.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>, и Add для добавления элемента. | IVector<T> и Append для добавления элемента. Если вы где-либо используете 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 по-прежнему сможет подключить его к событию ButtonBase.Click , созданному OpenButton.
В C++/WinRT метод OpenButton_Click должен быть общедоступным в типе реализации, если вы хотите зарегистрировать его в разметке XAML. Если вы регистрируете обработчик событий только в императивном коде, обработчик событий не должен быть общедоступным.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
void OpenButton_Click(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::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::Microsoft::UI::Xaml::RoutedEventArgs const& args);
}
};
Еще один заключительный сценарий — это когда проект C#, который вы переносите, привязывает обработчик событий из разметки (дополнительные сведения об этом сценарии см. в разделе Функции в x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
Вы можете просто изменить эту разметку на более простой Click="OpenButton_Click". Или, если хотите, можете сохранить эту разметку как есть. Все, что необходимо сделать для поддержки, заключается в объявлении обработчика событий в IDL.
void OpenButton_Click(Object sender, Microsoft.UI.Xaml.RoutedEventArgs e);
Note
Объявите функцию как void, даже если вы реализуете её как запустил и забыл.
Изменения, связанные с синтаксисом языка
| Category | 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 |
| Метод «запустил и забыл» (подразумевает асинхронность) | async void ... |
winrt::fire_and_forget ... |
Перенос метода CopyButton_Click, Запустил и забыл |
| Доступ к перечисленной константе | 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() |
|
| Строки |
string, или System.String |
winrt::hstring | Обработка строк в C++/WinRT |
| Строковый литерал | "a string literal" |
L"a string literal" |
Перенос конструктора, Current и FEATURE_NAME |
| Выведенный (или дедуцированный) тип | var |
auto |
Перенос метода BuildClipboardFormatsOutputString |
| Директива Using- | using A.B.C; |
using namespace A::B::C; |
Перенос кода конструктора, Current и FEATURE_NAME |
| Дословный или необработанный строковый литерал | @"verbatim string literal" |
LR"(raw string literal)" |
Перенос метода DisplayToast |
Note
Если файл заголовка не содержит директиву using namespace для заданного пространства имен, вам придется полностью указать все имена типов для этого пространства имен; или, по крайней мере, достаточно квалифицировать их для компилятора, чтобы найти их. Пример см. в разделе "Перенос метода DisplayToast".
Перенос классов и их членов
Для каждого типа C# необходимо решить, следует ли перенести его в тип среда выполнения Windows или в обычный класс C++, структуру или перечисление. Дополнительные сведения и подробные примеры, демонстрирующие принятие этих решений, см. в статье "Перенос примера буфера обмена на C++/WinRT" из C#.
Свойство C# обычно становится функцией доступа, функцией мутатора и элементом резервных данных. Дополнительные сведения и пример см. в разделе "Перенос свойства IsClipboardContentChangedEnabled".
Для нестатических полей сделайте их элементами данных типа реализации.
Статическое поле C# преобразуется в статическую функцию доступа и/или статическую функцию-мутатор C++/WinRT. Дополнительные сведения и пример см. в статье "Перенос конструктора", "Current" и FEATURE_NAME.
Для функций-членов, опять же, необходимо для каждой из них решить, следует ли включать её в IDL или же она является открытым либо закрытым методом вашего типа реализации. Дополнительные сведения и примеры решения см. в IDL для типа MainPage.
Перенос разметки XAML и файлов ресурсов
В случае переноса примера буфера обмена на C++/WinRT из C#, мы смогли использовать ту же разметку XAML (включая ресурсы) и файлы ресурсов в C# и проекте C++/WinRT. В некоторых случаях изменения разметки потребуются для этого. См. Скопируйте XAML и стили, необходимые для завершения переноса MainPage.
Изменения, затрагивающие процедуры в языке
| Category | 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; |
Перенос конструктора, Current и FEATURE_NAME |
| Создание объекта в переменную с помощью args | 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 |
| Создание объекта в переменную без args | 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);Для строк используйте StringBuilder |
|
| 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 existingv = map[k]; // throws if not presentmap.ContainsKey(k) |
map.Insert(k, v); // replaces any existingv = map.Lookup(k); // throws if not presentmap.HasKey(k) |
|
| Преобразование типов (вызов сбоя) | (MyType)v |
v.as<MyType>() |
Перенос метода Footer_Click |
| Преобразование типов (null в случае ошибки) | v as MyType |
v.try_as<MyType>() |
Перенос метода PasteButton_Click |
| XAML-элементы с x:Name — это свойства | MyNamedElement |
MyNamedElement() |
Перенос конструктора, Current и FEATURE_NAME |
| Переключитесь на поток пользовательского интерфейса | CoreDispatcher.RunAsync | DispatcherQueue.TryEnqueue или winrt::resume_foreground | Перенос метода NotifyUser и перенос метода HistoryAndRoaming |
| Создание элемента пользовательского интерфейса в императивном коде на странице XAML | См. конструкцию элементов пользовательского интерфейса | См. конструкцию элементов пользовательского интерфейса |
В следующих разделах рассматриваются более подробные сведения о некоторых элементах таблицы.
Создание элемента пользовательского интерфейса
В этих примерах кода показано построение элемента пользовательского интерфейса в императивном коде страницы XAML.
var myTextBlock = new TextBlock()
{
Text = "Text",
Style = (Microsoft.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
winrt::unbox_value<Microsoft::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 для ограниченного количества типов. Вам потребуется добавить перегрузки для любых дополнительных типов, которые требуется строковать.
| Language | Преобразовать 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 .
| Category | C# | C++/WinRT |
|---|---|---|
| Формирование строк | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
| Добавить строку среда выполнения Windows с сохранением нулевых символов | builder.Append(s); |
builder << std::wstring_view{ s }; |
| Добавить новую строку | builder.Append(Environment.NewLine); |
builder << std::endl; |
| Доступ к результату | s = builder.ToString(); |
ws = builder.str(); |
Также см. раздел "Перенос метода BuildClipboardFormatsOutputString" и перенос метода DisplayChangedFormats.
Выполнение кода в основном потоке пользовательского интерфейса
Этот пример взят из примера сканера штрихкодов.
Если вы хотите работать с основным потоком пользовательского интерфейса в проекте C#, обычно используется метод DispatcherQueue.TryEnqueue (или более старый CoreDispatcher.RunAsync в UWP). Вот как выглядит шаблон в C#.
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
DispatcherQueue.TryEnqueue(() =>
{
// 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 DispatcherQueue();
// Do work on the main UI thread here.
}
Если вам нужно выполнить работу по приоритету, отличному от значения по умолчанию, см. функцию winrt::resume_foreground , которая имеет перегрузку, которая принимает приоритет. Примеры кода, показывающие, как ожидать вызова winrt::resume_foreground, см. в статье Программирование с учетом сходства потоков.
Задачи по переносу, специфичные для C++/WinRT
Определение классов среды выполнения в IDL
См. IDL для типа MainPage и объедините свои .idl файлы.
Включите необходимые файлы заголовков пространства имён Windows для C++/WinRT
В C++/WinRT каждый раз, когда требуется использовать тип из пространства имён Windows, необходимо подключить соответствующий заголовочный файл пространства имён Windows для C++/WinRT. Пример см. в разделе "Перенос метода NotifyUser".
Бокс и распаковка
C# автоматически упаковывает скаляры в объекты. C++/WinRT требует явного вызова функции winrt::box_value . В обоих языках требуется явная распаковка. См. Боксинг и анбоксинг в C++/WinRT.
В следующих таблицах мы будем использовать эти определения.
| C# | C++/WinRT |
|---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
| Operation | C# | C++/WinRT |
|---|---|---|
| Боксинг | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| Распаковки | i = (int)o;s = (string)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
C++/CX и C# генерируют исключение, если вы попытаетесь распаковать нулевой указатель в тип значения. C++/WinRT считает это ошибкой программирования и аварийно завершает работу. В C++/WinRT используйте функцию winrt::unbox_value_or , если вы хотите обработать ситуацию, когда объект не относится к типу, который вы подумали.
| Scenario | C# | C++/WinRT |
|---|---|---|
| Распаковать известное целочисленное значение | i = (int)o; |
i = unbox_value<int>(o); |
| Если o имеет значение NULL | System.NullReferenceException |
Авария |
| Если o не является упакованным значением типа int | System.InvalidCastException |
Авария |
| Распаковать int, использовать значение по умолчанию, если null; завершиться с ошибкой во всех остальных случаях | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Если возможно, распакуйте int; для всего остального используйте резервный вариант | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Пример см. в разделе "Перенос метода OnNavigatedTo" и перенос метода Footer_Click.
Бокс и распаковка строки
Строка в некотором смысле является типом значения и другими способами ссылочным типом. C# и C++/WinRT обрабатывают строки по-разному.
Тип ABI HSTRING — это указатель на строку с подсчётом ссылок. Но он не является производным от IInspectable, поэтому это не технически объект. Кроме того, HSTRING со значением null представляет собой пустую строку. Упаковка объектов, не наследуемых от IInspectable, выполняется путем помещения их в оболочку IReference<T>, а среда выполнения Windows предоставляет стандартную реализацию в виде объекта PropertyValue (пользовательские типы обозначаются как PropertyType::OtherType).
C# представляет строку среда выполнения Windows в качестве ссылочного типа. В то время как C++/WinRT проектирует строку как тип значения. Это означает, что прямоугольная строка NULL может иметь различные представления в зависимости от того, как вы там попали.
| Behavior | 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 (допустимо) |
| Если вы присваиваете объекту пустую строку | o = (string)null;o == null |
o = box_value(hstring{});o != nullptr |
Если вы назначаете "" объекту |
o = "";o != null |
o = box_value(hstring{L""});o != nullptr |
Базовый бокс и распаковка.
| Operation | C# | C++/WinRT |
|---|---|---|
| Упаковать строку | o = s;Пустая строка становится ненулевой объектом. |
o = box_value(s);Пустая строка становится ненулевой объектом. |
| Распаковать известную строку | s = (string)o;Объект NULL становится пустой строкой. InvalidCastException, если это не строка. |
s = unbox_value<hstring>(o);Происходит сбой объекта NULL. Происходит сбой, если значение не является строкой. |
| Распаковка возможной строки | s = o as string;Объект null или нестроковое значение становится строкой null. OR s = o as string ?? fallback;Значение NULL или нестроковое значение становится резервным значением. Пустая строка сохранена. |
s = unbox_value_or<hstring>(o, fallback);Значение NULL или нестроковое значение становится резервным значением. Пустая строка сохранена. |
Предоставление классу доступа к расширению разметки {Binding}
Если вы планируете использовать расширение разметки {Binding} для привязки к вашему типу данных, см. объект Binding, объявленный с помощью {Binding}.
Использование объектов, заданных в XAML-разметке
В проекте C# можно использовать закрытые члены и именованные элементы из разметки XAML. Но в C++/WinRT все сущности, потребляемые с помощью расширения разметки XAML {x:Bind} , должны предоставляться публично в IDL.
Кроме того, привязка к логическому значению отображается как true или false в C#, а в C++/WinRT — как Windows.Foundation.IReference`1<Boolean>.
Дополнительные сведения и примеры кода см. в разделе "Использование объектов из разметки".
Обеспечение доступности источника данных для разметки XAML
В C++/WinRT версии 2.0.190530.8 или более поздних версий winrt::single_threaded_observable_vector создает наблюдаемый вектор, который поддерживает как IObservableVector<T>, так и IObservableVector<IInspectable>. Пример см. в разделе "Перенос свойства "Сценарии".
Вы можете создать свой Midl-файл (.idl), например, так (см. также Выделение классов среды выполнения в файлы Midl (.idl)).
namespace Bookstore
{
runtimeclass BookSku { ... }
runtimeclass BookstoreViewModel
{
Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
runtimeclass MainPage : Microsoft.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>
- IBindableVector и INotifyCollectionChanged
- IBindableVector и IBindableObservableVector
- IBindableVector сам по себе (не будет реагировать на изменения)
- IVector<IInspectable>
- IBindableIterable (будет итерировать и сохранять элементы в частной коллекции)
Универсальный интерфейс, например IVector<T> , не может быть обнаружен во время выполнения. Каждый IVector<T> имеет другой идентификатор интерфейса (IID), который является функцией T. Любой разработчик может произвольно расширить набор T , поэтому ясно, что код привязки XAML никогда не может знать полный набор запросов. Это ограничение не является проблемой для C#, так как каждый объект CLR, реализующий IEnumerable<T> , автоматически реализует 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, вам потребуется вернуться в 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.
unsealed runtimeclass BasePage : Microsoft.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
Связанные темы
Windows developer