Pindah ke C++/WinRT dari C#

Tip

Jika Anda telah membaca topik ini sebelumnya, dan Anda kembali ke topik ini dengan mempertimbangkan tugas tertentu, maka Anda dapat melompat ke bagian Temukan konten berdasarkan tugas yang Anda lakukan di topik ini.

Topik ini mendokumentasikan secara komprehensif detail teknis yang terlibat dalam mem-porting kode sumber dalam proyek C# ke padanannya dalam C++/WinRT.

Untuk studi kasus tentang porting salah satu sampel aplikasi Universal Windows Platform (UWP), lihat topik pendamping Memindahkan sampel Clipboard ke C++/WinRT dari C#. Anda dapat memperoleh latihan dan pengalaman melakukan porting dengan mengikuti panduan langkah demi langkah tersebut, serta melakukan porting pada sampelnya sendiri seiring Anda mengikutinya.

Cara mempersiapkan, dan apa yang diharapkan

Studi kasus Memindahkan sampel Clipboard ke C++/WinRT dari C# menggambarkan contoh jenis keputusan desain perangkat lunak yang akan Anda buat saat memindahkan proyek ke C++/WinRT. Jadi, ada baiknya mempersiapkan porting dengan mendapatkan pemahaman yang kuat tentang cara kerja kode yang ada. Dengan begitu, Anda akan mendapatkan gambaran umum yang baik tentang fungsionalitas aplikasi, dan struktur kode, dan kemudian keputusan yang Anda buat akan selalu membawa Anda maju, dan ke arah yang benar.

Terkait jenis perubahan dalam proses porting yang dapat diharapkan, Anda dapat mengelompokkannya ke dalam empat kategori.

  • Port proyeksi bahasa. Windows Runtime (WinRT) diproyeksikan ke dalam berbagai bahasa pemrograman. Setiap proyeksi bahasa tersebut dirancang agar terasa alami dalam bahasa pemrograman yang bersangkutan. Untuk C#, beberapa jenis Windows Runtime diproyeksikan sebagai jenis .NET. Jadi, misalnya Anda akan menerjemahkan System.Collections.Generic.IReadOnlyList<T> kembali ke Windows. Foundation.Collections.IVectorView<T>. Juga dalam C#, beberapa operasi Windows Runtime diproyeksikan sebagai fitur bahasa C# yang nyaman. Contohnya adalah bahwa dalam C# Anda menggunakan += sintaks operator untuk mendaftarkan delegasi penanganan peristiwa. Jadi Anda akan menerjemahkan fitur bahasa seperti itu kembali ke operasi mendasar yang sedang dilakukan (pendaftaran peristiwa, dalam contoh ini).
  • Sintaks bahasa port. Banyak dari perubahan ini adalah transformasi mekanis sederhana, menggantikan satu simbol untuk yang lain. Misalnya, mengubah titik (.) menjadi titik dua (::).
  • Prosedur bahasa port. Beberapa di antaranya bisa menjadi perubahan sederhana dan berulang (seperti myObject.MyPropertymyObject.MyProperty()). Yang lain membutuhkan perubahan yang lebih dalam (misalnya, memindahkan prosedur yang melibatkan penggunaan System.Text.StringBuilder ke salah satu yang melibatkan penggunaan std::wostringstream).
  • Tugas terkait porting yang khusus untuk C++/WinRT. Detail tertentu dari Windows Runtime diurus secara implisit oleh C#, di belakang layar. Detail tersebut dilakukan secara eksplisit di C++/WinRT. Contohnya adalah Anda menggunakan .idl file untuk menentukan kelas runtime Anda.

Setelah indeks berbasis tugas yang mengikuti, bagian lainnya dalam topik ini disusun sesuai dengan taksonomi di atas.

Menemukan konten berdasarkan tugas yang Anda lakukan

Tugas Content
Menulis komponen Windows Runtime (WRC) Fungsionalitas tertentu dapat dicapai (atau API tertentu yang disebut) hanya dengan C++. Anda dapat memperhitungkan fungsionalitas tersebut ke dalam C++/WinRT WRC, lalu menggunakan WRC dari (misalnya) aplikasi C#. Lihat Komponen Windows Runtime dengan C++/WinRT dan Jika Anda membuat kelas runtime dalam komponen Windows Runtime.
Adaptasikan metode asinkron Sebaiknya, baris pertama dari metode asinkron dalam kelas runtime C++/WinRT adalah auto lifetime = get_strong(); (lihat Mengakses pointer this dengan aman dalam coroutine anggota kelas).

Melakukan porting dari Task, lihat Aksi asinkron.
Mem-porting dari Task<T>, lihat Operasi asinkron.
Memindahkan dari async void, lihat metode fire-and-forget.
Mem-porting kelas Pertama, tentukan apakah kelas perlu menjadi kelas runtime, atau apakah itu bisa menjadi kelas biasa. Untuk membantu Anda memutuskan hal itu, lihat bagian paling awal dari Membuat API dengan C++/WinRT. Kemudian, lihat tiga baris berikutnya di bawah ini.
Port kelas runtime Kelas yang berbagi fungsionalitas di luar aplikasi C++, atau kelas yang digunakan dalam pengikatan data XAML. Lihat Jika Anda menulis kelas runtime dalam komponen Windows Runtime, atau Jika Anda menulis kelas runtime untuk direferensikan di antarmuka pengguna XAML Anda.

Tautan tersebut menjelaskannya dengan lebih rinci, tetapi runtime class harus dideklarasikan dalam IDL. Jika project Anda sudah berisi file IDL (misalnya, Project.idl), kami sarankan Anda mendeklarasikan kelas runtime baru dalam file tersebut. Di IDL, deklarasikan metode dan anggota data apa pun yang akan digunakan di luar aplikasi Anda, atau yang akan digunakan di XAML. Setelah memperbarui file IDL, membangun kembali, dan melihat file stub yang dihasilkan (.h dan .cpp) di folder proyek Generated Files Anda (Dalam Penjelajah Solusi, dengan simpul proyek dipilih, pastikan Perlihatkan Semua File diaktifkan). Bandingkan file stub dengan file yang sudah ada di proyek Anda, menambahkan file atau menambahkan/memperbarui tanda tangan fungsi seperlunya. Sintaks file stub selalu benar, jadi kami sarankan Anda menggunakannya untuk meminimalkan kesalahan build. Setelah stub dalam proyek Anda cocok dengan yang ada di file stub, Anda dapat melanjutkan dan mengimplementasikannya dengan memindahkan kode C#.
Port kelas biasa Lihat Jika Anda tidak membuat kelas runtime.
IDL Penulis Pengantar Microsoft Bahasa Definisi Antarmuka 3.0
Jika Anda menulis kelas runtime yang akan direferensikan di antarmuka pengguna XAML Anda
Menggunakan objek dari markup XAML
Menentukan kelas runtime Anda di IDL
Memindahkan koleksi Koleksi dengan C++/WinRT
Membuat sumber data tersedia untuk markup XAML
Kontainer asosiatif
Akses anggota vektor
Pindahkan acara Delegat penangan event sebagai anggota kelas
Mencabut delegasi penanganan aktivitas
Mem-porting sebuah metode Dari C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Ke file C++/WinRT .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Ke dalam file C++/WinRT .cpp: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
String port Penanganan string di C++/WinRT
ToString
Membangun string
Tinju dan membuka kotak string
Konversi jenis (pengecoran jenis) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Lihat juga ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Melempar jika pembukaan kotak gagal. Lihat juga Boxing dan unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Mengembalikan fallback jika pembatalan kotak gagal. Lihat juga Boxing dan unboxing.

C#: (Class)o
C++/WinRT: o.as<Class>()
Melempar jika konversi gagal.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Mengembalikan null jika konversi gagal.

Perubahan yang melibatkan proyeksi bahasa

Category C# C++/WinRT Baca juga
Objek tanpa tipe object, atau System.Object Windows::Foundation::IInspectable Memigrasikan metode EnableClipboardContentChangedNotifications
Namespace proyeksi using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Ukuran koleksi collection.Count collection.Size() Memporting metode BuildClipboardFormatsOutputString
Jenis koleksi umum IList<T>, dan Tambahkan untuk menambahkan elemen. IVector<T>, dan Tambahkan untuk menambahkan elemen. Jika Anda menggunakan std::vector di bagian mana pun, maka gunakan push_back untuk menambahkan elemen.
Jenis koleksi baca-saja IReadOnlyList<T> IVectorView<T> Memporting metode BuildClipboardFormatsOutputString
Delegasi penangan kejadian sebagai anggota kelas myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Melakukan porting metode EnableClipboardContentChangedNotifications
Mencabut delegasi penanganan aktivitas myObject.EventName -= Handler; myObject.EventName(token); Memigrasikan metode EnableClipboardContentChangedNotifications
Kontainer asosiatif IDictionary<K, V> IMap<K, V>
Akses anggota vektor x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Mendaftarkan/mencabut penanganan aktivitas

Di C++/WinRT, Anda memiliki beberapa opsi sintaks untuk mendaftarkan/membatalkan delegasi penanganan peristiwa, seperti yang dijelaskan dalam Menangani peristiwa dengan menggunakan delegasi di C++/WinRT. Lihat juga tentang mem-porting metode EnableClipboardContentChangedNotifications.

Terkadang, misalnya ketika penerima peristiwa (objek yang menangani peristiwa) akan dihancurkan, Anda mungkin ingin membatalkan penangan peristiwa agar sumber peristiwa (objek yang memicu peristiwa) tidak memanggil objek yang sudah dihancurkan. Lihat Mencabut delegasi terdaftar. Dalam kasus seperti itu, buat variabel anggota event_token untuk penanganan aktivitas Anda. Sebagai contoh, lihat Mem-porting metode EnableClipboardContentChangedNotifications.

Anda juga dapat mendaftarkan penanganan aktivitas di markup XAML.

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

Di C#, metode OpenButton_Click Anda dapat bersifat privat, dan XAML masih dapat menghubungkannya ke peristiwa ButtonBase.Click yang dinaikkan oleh OpenButton.

Di C++/WinRT, metode OpenButton_Click Anda harus publik dalam jenis implementasiAnda jika Anda ingin mendaftarkannya di markup XAML. Jika Anda mendaftarkan penanganan aktivitas hanya dalam kode imperatif, maka penanganan aktivitas tidak perlu publik.

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

Atau Anda dapat menjadikan halaman XAML yang mendaftarkan sebagai friend bagi tipe implementasi Anda, dan OpenButton_Click sebagai private.

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);
    }
};

Salah satu skenario terakhir adalah di mana proyek C# yang Anda porting mengikat ke penanganan aktivitas dari markup (untuk latar belakang lainnya tentang skenario tersebut, lihat Fungsi dalam x:Bind).

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

Anda hanya bisa mengubah markup itu menjadi lebih sederhana Click="OpenButton_Click". Atau, jika mau, Anda dapat menyimpan markup tersebut apa adanya. Yang harus Anda lakukan untuk mendukungnya adalah mendeklarasikan penanganan aktivitas di IDL.

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

Note

Deklarasikan fungsi tersebut sebagai void meskipun Anda mengimplementasikannya sebagai Fire and forget.

Perubahan yang melibatkan sintaks bahasa

Category C# C++/WinRT Baca juga
Pengubah akses public \<member\> public:
    \<member\>
Porting metode Button_Click
Mengakses anggota data this.variable this->variable  
Tindakan asinkron async Task ... IAsyncAction ... Antarmuka IAsyncAction, Konkurensi, dan operasi asinkron dengan C++/WinRT
Operasi asinkron async Task<T> ... IAsyncOperation<T> ... IAsyncOperation antarmuka, Konkurensi dan operasi asinkron dengan C++/WinRT
Metode Fire-and-forget (menyiratkan asinkron) async void ... winrt::fire_and_forget ... Mem-porting metode CopyButton_Click, Jalankan lalu lupakan
Mengakses konstanta bernilai enumerasi E.Value E::Value Memindahkan metode DisplayChangedFormats
Menunggu secara kooperatif await ... co_await ... Porting metode CopyButton_Click
Kumpulan jenis yang diproyeksikan sebagai bidang privat private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Pembuatan 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} };
Pemisah namespace A.B.T A::B::T
Null null nullptr Memindahkan metode UpdateStatus
Mendapatkan objek jenis typeof(MyType) winrt::xaml_typename<MyType>() Porting properti Skenario
Deklarasi parameter untuk metode MyType MyType const& Pengoperasian parameter
Deklarasi parameter untuk metode asinkron MyType MyType Pengoperasian parameter
Memanggil metode statis T.Method() T::Method()
String string, atau System.String winrt::hstring Penanganan string di C++/WinRT
String literal (teks literal) "a string literal" L"a string literal" Memindahkan konstruktor, Saat Ini, dan FEATURE_NAME
Tipe yang disimpulkan (atau dideduksi) var auto Memporting metode BuildClipboardFormatsOutputString
Menggunakan direktif using A.B.C; using namespace A::B::C; Memindahkan konstruktor, Saat Ini, dan FEATURE_NAME
Verbatim/string mentah literal @"verbatim string literal" LR"(raw string literal)" Porting metode DisplayToast

Note

Jika file header tidak berisi direktif using namespace untuk namespace tertentu, Anda harus menuliskan semua nama tipe dalam namespace tersebut secara lengkap; atau setidaknya menuliskannya dengan kualifikasi yang cukup agar kompilator dapat menemukannya. Misalnya, lihat Mem-porting metode DisplayToast.

Pemortingan kelas dan anggota

Anda harus memutuskan, untuk setiap jenis C#, apakah akan memindahkannya ke jenis Windows Runtime, atau ke kelas/struktur/enumerasi C++ biasa. Untuk informasi selengkapnya, dan contoh terperinci yang mengilustrasikan cara membuat keputusan tersebut, lihat Memindahkan sampel Clipboard ke C++/WinRT dari C#.

Properti C# biasanya menjadi fungsi pengakses, fungsi mutator, dan anggota data cadangan. Untuk informasi selengkapnya, dan contohnya, lihat Porting properti IsClipboardContentChangedEnabled.

Untuk bidang non-statis, jadikan mereka anggota data dari jenis implementasi Anda.

Bidang statis C# menjadi aksesor statis C++/WinRT dan/atau fungsi mutator. Untuk informasi selengkapnya, dan contohnya, lihat Memindahkan konstruktor, Saat Ini, dan FEATURE_NAME.

Untuk fungsi anggota, sekali lagi, Anda harus memutuskan untuk masing-masing apakah fungsi tersebut termasuk dalam IDL atau tidak, atau apakah itu fungsi anggota publik atau privat dari jenis implementasi Anda. Untuk informasi selengkapnya, dan contoh cara menentukan, lihat IDL untuk tipe MainPage.

Mem-porting markup XAML dan file aset

Dalam kasus Porting sampel Clipboard ke C++/WinRT dari C#, kami dapat menggunakan markup XAML yang sama (termasuk sumber daya) dan file aset di seluruh C# dan proyek C++/WinRT. Dalam beberapa kasus, pengeditan markup mungkin diperlukan untuk mencapainya. Lihat Menyalin XAML dan gaya yang diperlukan untuk menyelesaikan port MainPage.

Perubahan yang melibatkan prosedur dalam bahasa

Category C# C++/WinRT Baca juga
Manajemen seumur hidup dalam metode asinkron N/A auto lifetime{ get_strong() }; atau
auto lifetime = get_strong();
Porting metode CopyButton_Click
Pembuangan using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Porting metode CopyImage
Membuat objek new MyType(args) MyType{ args } atau
MyType(args)
Porting properti Skenario
Membuat referensi yang tidak diinisialisasi MyType myObject; MyType myObject{ nullptr }; atau
MyType myObject = nullptr;
Memindahkan konstruktor, Saat Ini, dan FEATURE_NAME
Membuat objek menjadi variabel dengan args var myObject = new MyType(args); auto myObject{ MyType{ args } }; atau
auto myObject{ MyType(args) }; atau
auto myObject = MyType{ args }; atau
auto myObject = MyType(args); atau
MyType myObject{ args }; atau
MyType myObject(args);
Porting metode Footer_Click
Buat objek menjadi variabel tanpa arg var myObject = new T(); MyType myObject; Memporting metode BuildClipboardFormatsOutputString
Bentuk singkat inisialisasi objek var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Operasi vektor massal var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Porting metode CopyButton_Click
Lakukan iterasi pada koleksi foreach (var v in c) for (auto&& v : c) Memporting metode BuildClipboardFormatsOutputString
Menangkap pengecualian catch (Exception ex) catch (winrt::hresult_error const& ex) Porting metode PasteButton_Click
Detail pengecualian ex.Message ex.message() Porting metode PasteButton_Click
Dapatkan nilai properti myObject.MyProperty myObject.MyProperty() Porting metode NotifyUser
Mengatur nilai properti myObject.MyProperty = value; myObject.MyProperty(value);
Menaikkan nilai properti myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Untuk string, beralihlah ke builder
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
String bahasa ke string Windows Runtime N/A winrt::hstring{ s }
Perakitan string StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Membangun string
Interpolasi string $"{i++}) {s.Title}" winrt::to_hstring, dan/atau winrt::hstring::operator+ Porting metode OnNavigatedTo
String kosong untuk perbandingan System.String.Empty winrt::hstring::empty Memindahkan metode UpdateStatus
Membuat string kosong var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Operasi kamus 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)
Konversi tipe (hasilkan pengecualian jika gagal) (MyType)v v.as<MyType>() Porting metode Footer_Click
Konversi tipe (null jika gagal) v as MyType v.try_as<MyType>() Memindahkan metode PasteButton_Click
Elemen XAML dengan x:Name adalah properti MyNamedElement MyNamedElement() Memindahkan konstruktor, Saat Ini, dan FEATURE_NAME
Beralih ke thread UI CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue, atau winrt::resume_foreground Mem-porting metode NotifyUser, dan mem-porting metode HistoryAndRoaming
Konstruksi elemen UI dalam kode imperatif di halaman XAML Lihat konstruksi elemen UI Lihat konstruksi elemen UI

Bagian berikut masuk ke detail lebih lanjut mengenai beberapa item dalam tabel.

Pembuatan elemen UI

Contoh kode ini menunjukkan konstruksi elemen UI dalam kode imperatif halaman 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()

Jenis C# menyediakan metode Object.ToString .

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

C++/WinRT tidak secara langsung menyediakan fasilitas ini, tetapi Anda dapat beralih ke alternatif.

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

C++/WinRT juga mendukung winrt::to_hstring untuk sejumlah jenis yang terbatas. Anda harus menambahkan kelebihan beban untuk jenis tambahan apa pun yang ingin Anda stringifikasi.

Bahasa Konversi int menjadi string Ubah enum menjadi string
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);

Jika Anda mengonversi enum menjadi string, Anda perlu menyediakan implementasi 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));
        }
    }
}

Hasil konversi menjadi string ini sering dimanfaatkan secara implisit oleh mekanisme data binding.

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

Pengikatan ini akan menerapkan winrt::to_hstring pada properti yang diikat. Dalam kasus contoh kedua ( StatusEnum), Anda harus menyediakan kelebihan beban Winrt::to_hstring Anda sendiri, jika tidak, Anda akan mendapatkan kesalahan kompilator.

Lihat juga Porting metode Footer_Click.

Pembuatan string

Untuk pembuatan string, C# memiliki jenis StringBuilder bawaan.

Category C# C++/WinRT
Perakitan string StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Menambahkan string Windows Runtime dengan mempertahankan nilai null builder.Append(s); builder << std::wstring_view{ s };
Menambahkan baris baru builder.Append(Environment.NewLine); builder << std::endl;
Mengakses hasil s = builder.ToString(); ws = builder.str();

Lihat juga Porting metode BuildClipboardFormatsOutputString, dan Porting metode DisplayChangedFormats.

Menjalankan kode pada utas UI utama

Contoh ini diambil dari sampel pemindai Barcode.

Ketika Anda ingin melakukan pekerjaan pada utas UI utama dalam proyek C#, Anda biasanya menggunakan metode DispatcherQueue.TryEnqueue (atau CoreDispatcher.RunAsync yang lebih lama di UWP). Seperti inilah polanya dalam C#.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        // Do work on the main UI thread here.
    });
}

Jauh lebih mudah untuk mengekspresikannya di C++/WinRT. Perhatikan bahwa kita menerima parameter secara nilai dengan asumsi bahwa kita akan perlu mengaksesnya setelah titik penangguhan eksekusi pertama (co_await, dalam hal ini). Untuk informasi selengkapnya, lihat Parameter-passing.

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

Jika Anda perlu melakukan pekerjaan dengan prioritas selain default, lihat fungsi winrt::resume_foreground , yang memiliki kelebihan beban yang mengambil prioritas. Untuk contoh kode yang menunjukkan cara menunggu panggilan ke winrt::resume_foreground, lihat Pemrograman dengan afinitas utas.

Menentukan kelas runtime Anda di IDL

Lihat IDL untuk tipe MainPage, dan Konsolidasikan file .idlAnda.

Sertakan file header namespace C++/WinRT Windows yang Anda butuhkan

Di C++/WinRT, setiap kali Anda ingin menggunakan jenis dari namespace Windows, Anda perlu menyertakan file header namespace C++/WinRT Windows yang sesuai. Misalnya, lihat Mem-porting metode NotifyUser.

Boxing dan Unboxing

C# secara otomatis mengetikkan skalar ke dalam objek. C++/WinRT mengharuskan Anda untuk memanggil fungsi winrt::box_value secara eksplisit. Kedua bahasa mengharuskan Anda untuk membuka kotak secara eksplisit. Lihat Tinju dan buka kotak dengan C++/WinRT.

Dalam tabel berikut, kita akan menggunakan definisi ini.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# C++/WinRT
Boxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Pembukaan Kotak i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX dan C# memunculkan pengecualian jika Anda mencoba melakukan unboxing pada penunjuk null menjadi tipe nilai. C++/WinRT menganggap ini sebagai kesalahan pemrograman, dan mengalami crash. Di C++/WinRT, gunakan fungsi winrt::unbox_value_or jika Anda ingin menangani kasus di mana objek bukan dari jenis yang Anda pikirkan.

Scenario C# C++/WinRT
Lakukan unboxing pada integer yang diketahui i = (int)o; i = unbox_value<int>(o);
Jika o null System.NullReferenceException Kecelakaan
Jika o bukan int yang dibungkus System.InvalidCastException Kecelakaan
Batalkan kotak masuk, gunakan fallback jika null; crash jika ada yang lain i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Batalkan kotak int jika memungkinkan; gunakan fallback untuk hal lain i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Misalnya, lihat Porting metode OnNavigatedTo, dan Porting metode Footer_Click.

Tinju dan membuka kotak string

String dalam beberapa hal adalah sebuah tipe nilai, namun dalam hal lain merupakan tipe referensi. C# dan C++/WinRT memperlakukan string secara berbeda.

Tipe ABI HSTRING adalah pointer ke string dengan jumlah referensi yang dihitung. Tetapi tidak berasal dari IInspectable, jadi itu bukan objek secara teknis. Selain itu, HSTRING null mewakili string kosong. Proses boxing untuk objek yang tidak diturunkan dari IInspectable dilakukan dengan membungkusnya dalam IReference<T>, dan Windows Runtime menyediakan implementasi standar dalam bentuk objek PropertyValue (jenis kustom dilaporkan sebagai PropertyType::OtherType).

C# mewakili string Windows Runtime sebagai jenis referensi; sementara C++/WinRT memproyeksikan string sebagai jenis nilai. Ini berarti bahwa string null kotak dapat memiliki representasi yang berbeda tergantung bagaimana Anda sampai di sana.

Behavior C# C++/WinRT
Deklarasi object o;
string s;
IInspectable o;
hstring s;
Kategori jenis string Jenis referensi Jenis nilai
null HSTRING diproyeksikan sebagai "" hstring{}
Apakah null dan "" identik? No Yes
Keabsahan null s = null;
s.Length melempar NullReferenceException
s = hstring{};
s.size() == 0 (valid)
Jika Anda menetapkan string null ke objek o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Jika Anda menetapkan "" ke objek o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Tinju dasar dan pembukaan kotak.

Operation C# C++/WinRT
Beri kotak pada teks o = s;
String kosong menjadi objek non-null.
o = box_value(s);
String kosong menjadi objek non-null.
Batalkan kotak string yang diketahui s = (string)o;
Objek null menjadi string null.
InvalidCastException jika nilainya bukan string.
s = unbox_value<hstring>(o);
Objek null mengalami crash.
Crash jika nilainya bukan string.
Batalkan kotak kemungkinan string s = o as string;
Objek null atau non-string menjadi string null.

OR

s = o as string ?? fallback;
Null atau bukan string akan menjadi nilai cadangan.
String kosong dipertahankan.
s = unbox_value_or<hstring>(o, fallback);
Null atau bukan string akan menjadi nilai cadangan.
String kosong dipertahankan.

Membuat kelas tersedia untuk ekstensi markup {Binding}

Jika Anda bermaksud menggunakan ekstensi markup {Binding} untuk mengikat data ke tipe data Anda, lihat objek Binding yang dideklarasikan menggunakan {Binding}.

Mengkonsumsi objek dari markup XAML

Dalam proyek C#, Anda dapat mengakses anggota privat dan elemen yang diberi nama dari markup XAML. Tetapi di C++/WinRT, semua entitas yang digunakan dengan menggunakan ekstensi markup XAML {x:Bind} harus diekspos secara publik di IDL.

Selain itu, pengikatan ke Boolean ditampilkan sebagai true atau false di C#, tetapi di C++/WinRT ditampilkan sebagai Windows.Foundation.IReference`1<Boolean>.

Untuk informasi selengkapnya, dan contoh kode, lihat Menggunakan objek dari markup.

Membuat sumber data tersedia untuk markup XAML

Di C++/WinRT versi 2.0.190530.8 atau yang lebih baru, winrt::single_threaded_observable_vector membuat vektor yang dapat diamati yang mendukung IObservableVector<T> dan IObservableVector<IInspectable>. Misalnya, lihat Porting properti Skenario.

Anda dapat menulis file Midl Anda (.idl) seperti ini (juga lihat Memperhitungkan kelas runtime ke dalam file 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; };
    }
}

Dan implementasikan seperti ini.

// 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;
};
...

Untuk informasi selengkapnya, lihat Kontrol item XAML; ikat ke koleksi C++/WinRT, dan Koleksi dengan C++/WinRT.

Membuat sumber data tersedia untuk markup XAML (sebelum C++/WinRT 2.0.190530.8)

Pengikatan data XAML mengharuskan sumber item mengimplementasikan IIterable<IInspectable>, serta salah satu dari kombinasi antarmuka berikut.

  • IObservableVector<IInspectable>
  • IBindableVector dan INotifyCollectionChanged
  • IBindableVector dan IBindableObservableVector
  • IBindableVector dengan sendirinya (tidak akan merespons perubahan)
  • IVector<IInspectable>
  • IBindableIterable (akan mengiterasi dan menyimpan elemen dalam koleksi privat)

Antarmuka generik seperti IVector<T> tidak dapat dideteksi saat runtime. Setiap IVector<T> memiliki pengidentifikasi antarmuka (IID) yang berbeda, yang merupakan fungsi T. Setiap pengembang dapat memperluas set T secara sembrono, jadi dengan jelas kode pengikatan XAML tidak pernah dapat mengetahui set lengkap untuk kueri. Pembatasan itu bukan masalah untuk C# karena setiap objek CLR yang mengimplementasikan IEnumerable<T> secara otomatis menerapkan IEnumerable. Pada tingkat ABI, itu berarti bahwa setiap objek yang mengimplementasikan IObservableVector<T> secara otomatis mengimplementasikan IObservableVector<IInspectable>.

C++/WinRT tidak menawarkan jaminan tersebut. Jika kelas runtime C++/WinRT mengimplementasikan IObservableVector<T>, maka kami tidak dapat berasumsi bahwa implementasi IObservableVector<IInspectable> juga disediakan.

Oleh karena itu, berikut tampilan contoh sebelumnya yang perlu terlihat seperti ini.

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

Dan implementasinya.

// 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;
};
...

Jika anda perlu mengakses objek di m_bookSkus, maka anda harus QI mereka kembali ke 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;
}

Kelas turunan

Agar dapat mewarisi dari kelas runtime, kelas dasar harus dapat dikomposisikan. C# tidak mengharuskan Anda melakukan langkah-langkah khusus untuk membuat kelas Anda dapat dikomposisikan, tetapi C++/WinRT mengharuskannya. Anda menggunakan kata kunci yang tidak disegel untuk menunjukkan bahwa Anda ingin kelas Anda dapat digunakan sebagai kelas dasar.

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

Dalam file header untuk jenis implementasi, Anda harus menyertakan file header kelas dasar sebelum menyertakan header yang dibuat secara otomatis untuk kelas turunan. Jika tidak, Anda akan menemui kesalahan seperti "Penggunaan tipe ini sebagai ekspresi tidak sah".

// 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 penting