Áttérés C#-ről C++/WinRT-re

Tipp

Ha korábban már elolvasta ezt a témakört, és most egy adott feladathoz tér vissza hozzá, akkor közvetlenül a témakör Tartalom keresése az elvégzendő feladat alapján szakaszára ugorhat.

Ez a témakör átfogóan katalogizálja a C#- projekt forráskódjának C++/WinRT-beli megfelelőjéhez való portolásának technikai részleteit.

Az Univerzális Windows-platform (UWP) alkalmazásminták egyikének portolásával kapcsolatos esettanulmányért tekintse meg a vágólap mintájának C++/WinRT-ből C#-ból való portolásával foglalkozó társtémakört. Portolási gyakorlatra és tapasztalatra tehet szert, ha követi ezt az útmutatót, és közben önállóan is portolja a mintát.

A felkészülés és a várható teendők

Az esettanulmány, A Vágólap minta portolása C#-ból C++/WinRT-re, példákon keresztül mutatja be, milyen szoftvertervezési döntéseket kell meghoznia egy projekt C++/WinRT-re történő portolása során. Ezért érdemes felkészülni a portolásra, ha alapos ismereteket szerez a meglévő kód működéséről. Így jó áttekintést kaphat az alkalmazás funkcióiról és a kód szerkezetéről, majd a meghozott döntések mindig a megfelelő irányba viszik előre.

A várható adathordozási változások tekintetében négy kategóriába csoportosíthatja őket.

  • A nyelvi projekció portolása. A Windows-futtatókörnyezet (WinRT) különböző programozási nyelvekre van kivetítve. Mindegyik ilyen nyelvi leképezést úgy tervezték, hogy természetesnek hasson az adott programozási nyelvben. A C# esetében egyes Windows-futtatókörnyezet típusok .NET típusként vannak előrevetítve. Így például a System.Collections.Generic.IReadOnlyList<T> elemet visszafordítja erre: Windows.Foundation.Collections.IVectorView<T>. A C#-ban is néhány Windows-futtatókörnyezet művelet kényelmes C# nyelvi funkciókként van előrevetítve. Ilyen például, hogy a C#-ban az += operátor szintaxisával regisztrál egy eseménykezelő meghatalmazottat. Tehát az ilyen nyelvi elemeket vissza fogja vezetni az éppen végrehajtott alapműveletre (ebben a példában az eseményregisztrációra).
  • Portnyelv szintaxisa. Ezek közül sok egyszerű mechanikai átalakítás, amely egy szimbólumot cserél egy másikra. Például a pont () kettőspontra (.::) való módosítása.
  • Portnyelvi eljárás. Ezek némelyike lehet egyszerű, ismétlődő módosítás (például myObject.MyProperty : myObject.MyProperty()). Más esetek mélyebb módosításokat igényelnek (például egy System.Text.StringBuilder használatát magában foglaló eljárás átültetését egy olyanra, amely std::wostringstream használatát foglalja magában).
  • A C++/WinRT-hez kapcsolódó portolással kapcsolatos feladatok. A Windows-futtatókörnyezet bizonyos részleteiről a C# implicit módon gondoskodik a színfalak mögött. Ezek a részletek kifejezetten a C++/WinRT-ben érhetők el. Ilyen például, ha egy .idl fájlt használ a futtatókörnyezeti osztályok definiálásához.

Az alábbi feladatalapú index után a témakör többi szakasza a fenti osztályozás szerint van strukturálva.

Tartalom keresése az éppen elvégzett feladat alapján

tevékenység Content
Windows-futtatókörnyezet összetevő (WRC) létrehozása Bizonyos funkciók (vagy bizonyos API-k) csak C++-val érhetők el. Ezt a funkciót beleszámíthatja egy C++/WinRT WRC-be, majd felhasználhatja a WRC-t (például) egy C#-alkalmazásból. Lásd: Windows-futtatókörnyezet-összetevők a C++/WinRT használatával és Ha runtime-osztályt hoz létre egy Windows-futtatókörnyezet-összetevőben.
Aszinkron metódus átalakítása Jó ötlet, ha egy C++/WinRT-futtatókörnyezet osztályában az aszinkron metódus első sora auto lifetime = get_strong(); (lásd: A this mutató biztonságos elérése osztálytag-korutinban).

Migrálás innen: Task, lásd: aszinkron művelet.
A(z) Task<T> környezetből történő portoláshoz lásd: Aszinkron művelet.
Portoláskor innen: async void, lásd: tűz és felejtsd el módszer.
Osztály portolása Először állapítsa meg, hogy az osztálynak futtatókörnyezeti osztálynak kell-e lennie, vagy lehet-e egy szokásos osztály. Ennek eldöntéséhez tekintse meg a Author API-k kezdetét a C++/WinRT használatával. Ezután tekintse meg az alábbi három sort.
Futtatókörnyezeti osztály portolása Olyan osztály, amely a C++ alkalmazáson kívüli funkciókat osztja meg, vagy egy XAML-adatkötésben használt osztály. Tekintse meg, hogy egy futtatókörnyezeti osztályt hoz-e létre egy Windows-futtatókörnyezet összetevőben, vagy ha olyan futtatókörnyezeti osztályt hoz létre, amelyre hivatkozni szeretne az XAML felhasználói felületén.

Ezek a hivatkozások részletesebben ismertetik ezt, de egy futtatókörnyezeti osztályt idL-ben kell deklarálni. Ha a projekt már tartalmaz egy IDL-fájlt (például a Project.idl fájlt), azt javasoljuk, hogy az új futtatókörnyezeti osztályt abban a fájlban deklarálja. Az IDL-ben deklaráljon minden olyan metódust és adattagot, amelyet az alkalmazáson kívül fognak használni, vagy amelyeket az XAML-ben fognak használni. Miután frissítette az IDL-fájlt, építse újra, és tekintse meg a létrehozott csonkfájlokat (.hés .cpp) a projekt mappájában Generated Files (Megoldáskezelő a projektcsomópont ki van jelölve, és győződjön meg arról, hogy az Összes fájl megjelenítése kapcsoló be van kapcsolva). Hasonlítsa össze a csonkfájlokat a projektben már szereplő fájlokkal, adjon hozzá fájlokat, vagy szükség szerint adjon hozzá/frissítse a függvény-aláírásokat. A Stub-fájl szintaxisa mindig helyes, ezért azt javasoljuk, hogy a buildelési hibák minimalizálása érdekében használja. Ha a projektben lévő csonkok megegyeznek a csonkfájlokban találhatóakkal, a C#-kód áthordásával implementálhatja őket.
Szokásos osztály portja Lásd: Ha nem ír futtatásidejű osztályt.
Szerző IDL A Microsoft Interface Definition Language 3.0 bemutatása
Ha az XAML felhasználói felületén hivatkozni kívánt futtatókörnyezeti osztályt hoz létre
Objektumok használata XAML-jelölőnyelvből
Futtatókörnyezeti osztályok definiálása az IDL-ben
Gyűjtemény áttelepítése Gyűjtemények C++/WinRT használatával
Adatforrás elérhetővé tétele az XAML-korrektúra számára
Asszociatív tároló
Vektortag elérése
Esemény portja Eseménykezelő delegátus osztálytagként
Eseménykezelő delegált visszavonása
Metódus átültetése C#-ból: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
A C++/WinRT .h fájlhoz: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
A C++/WinRT .cpp fájlhoz: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Portkarakterláncok Sztringkezelés a C++/WinRT-ben
ToString
Sztringépítés
Sztring dobozolása és kicsomagolása
Típuskonvertálás (típus-öntés) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Lásd még : ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Dob, ha a kicsomagolás sikertelen. Lásd még: Boxing and unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Visszaesést ad vissza, ha a kicsomagolás sikertelen. Lásd még: Dobozolás és kicsomagolás.

C#: (Class)o
C++/WinRT: o.as<Class>()
Dob, ha az átalakítás sikertelen.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Null értéket ad vissza, ha az átalakítás sikertelen.

A nyelvi kivetítés részét képező módosítások

Category C# C++/WinRT Lásd még
Nem beírt objektum objectvagy System.Object Windows::Foundation::IInspectable Az EnableClipboardContentChangedNotifications metódus portolása
Vetületi névterek using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Gyűjtemény mérete collection.Count collection.Size() A BuildClipboardFormatsOutputString metódus portolása
Tipikus gyűjteménytípus IList<T> és Hozzáadás elem hozzáadásához. IVector<T> és Append elem hozzáadásához. Ha bárhol std::vector-t használ, akkor egy elem hozzáadásához használja a push_back függvényt.
Írásvédett gyűjteménytípus IReadOnlyList<T> IVectorView<T> A BuildClipboardFormatsOutputString metódus portolása
Eseménykezelő delegált osztálytagként myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Az EnableClipboardContentChangedNotifications metódus portolása
Eseménykezelő delegált visszavonása myObject.EventName -= Handler; myObject.EventName(token); Az EnableClipboardContentChangedNotifications metódus portolása
Asszociatív tároló IDictionary<K, V> IMap<K, V>
Vektortag elérése x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Eseménykezelő regisztrálása/visszavonása

A C++/WinRT-ben számos szintaktikai lehetőség közül választhat egy eseménykezelő delegált regisztrálására/visszavonására, a C++/WinRT-meghatalmazottak használatával az események kezelése című cikkben leírtak szerint. Lásd még az EnableClipboardContentChangedNotifications metódus portolását.

Előfordulhat például, hogy amikor egy esemény címzettje (egy eseményt kezelő objektum) megsemmisül, vissza kell vonnia egy eseménykezelőt, hogy az eseményforrás (az eseményt növelő objektum) ne hívjon fel megsemmisült objektumot. Lásd: A regisztrált delegált visszavonása. Ilyen esetekben hozzon létre egy event_token tagváltozót az eseménykezelők számára. Példaként lásd: Az EnableClipboardContentChangedNotifications metódus portolása.

Az eseménykezelőt az XAML-jelölőnyelvben is regisztrálhatja.

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

A C#-ban a OpenButton_Click metódus privát lehet, és az XAML továbbra is csatlakoztathatja az OpenButton által létrehozott ButtonBase.Click eseményhez.

A C++/WinRT-ben a OpenButton_Click metódusnak nyilvánosnak kell lennie a megvalósítási típusban, ha XAML-korrektúrában szeretné regisztrálni. Ha csak imperatív kódban regisztrál egy eseménykezelőt, akkor az eseménykezelőnek nem kell nyilvánosnak lennie.

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

Alternatív megoldásként a regisztráló XAML-oldalt az implementációtípus barátjává, az OpenButton_Click tagot pedig priváttá teheti.

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

Az egyik utolsó forgatókönyv az, amikor a portolt C# projekt a korrektúra alapján köti az eseménykezelőhöz (a forgatókönyv további hátterét lásd: Functions in x:Bind).

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

Egyszerűen módosíthatja azt a jelölést az egyszerűbb Click="OpenButton_Click" formára. Vagy, ha úgy szeretné, megtarthatja változatlanul ezt a jelölést. Ennek támogatásához mindössze annyit kell tennie, hogy deklarálja az eseménykezelőt az IDL-ben.

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

Note

Deklarálja a függvényt void még akkor is, ha implementáljaFire and forget módszerrel.

A nyelvi szintaxist érintő módosítások

Category C# C++/WinRT Lásd még
Hozzáférési módosítók public \<member\> public:
    \<member\>
A Button_Click metódus portolása
Adattag elérése this.variable this->variable  
Aszinkron művelet async Task ... IAsyncAction ... IAsyncAction interfész, Párhuzamosság és aszinkron műveletek a C++/WinRT-ben
Aszinkron művelet async Task<T> ... IAsyncOperation<T> ... IAsyncOperation interfész, Párhuzamosság és aszinkron műveletek C++/WinRT használatával
Fire-and-forget metódus (aszinkront jelent) async void ... winrt::fire_and_forget ... A CopyButton_Click metódus portolása, a Fire és a Forget
Enumerált állandó elérése E.Value E::Value A DisplayChangedFormats metódus portolása
Kooperatív várakozás await ... co_await ... A CopyButton_Click metódus portolása
Leképezett típusok gyűjteménye privát mezőként private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID-konstrukció 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} };
Névtér-elválasztó A.B.T A::B::T
Null null nullptr Az UpdateStatus metódus portolása
Típusobjektum beszerzése typeof(MyType) winrt::xaml_typename<MyType>() A Forgatókönyvek tulajdonságának portolása
Metódus paraméterdeklarációja MyType MyType const& Paraméterátadás
Paraméterdeklaráció aszinkron metódushoz MyType MyType Paraméterátadás
Statikus metódus meghívása T.Method() T::Method()
Húrok stringvagy System.String winrt::hstring Sztringkezelés a C++/WinRT-ben
Sztring-konstans "a string literal" L"a string literal" A konstruktor, a Current és a FEATURE_NAME átültetése
Kikövetkeztetett (vagy dedukált) típus var auto A BuildClipboardFormatsOutputString metódus portolása
Irányelv használata using A.B.C; using namespace A::B::C; A konstruktor, a Current és a FEATURE_NAME átültetése
Verbatim/nyers sztringkonstans @"verbatim string literal" LR"(raw string literal)" A DisplayToast metódus portolása

Note

Ha egy fejlécfájl nem tartalmaz using namespace egy adott névtérre vonatkozó direktívát, akkor az adott névtér összes típusnevét teljes mértékben minősíteni kell, vagy legalább megfelelőnek kell lennie ahhoz, hogy a fordító megtalálja őket. Például lásd: DisplayToast metódus portolása.

Osztályok és tagok portolása

Minden C#-típus esetében el kell döntenie, hogy egy Windows-futtatókörnyezet típusba vagy egy normál C++ osztályba/struct/enumerálásba szeretné-e portolni. További információkért és részletes példákért, amelyek bemutatja, hogyan hozhatja meg ezeket a döntéseket, tekintse meg a vágólap mintájának C++/WinRT-fájlba való portolását a C#-ból.

A C#-tulajdonság általában egy kiegészítő függvény, egy mutációs függvény és egy háttéradat-tag lesz. További információ és példa: IsClipboardContentChangedEnabled tulajdonság portolása.

Nem statikus mezők esetén tegye őket a megvalósítási típus adattagjaivá.

A C# statikus mező C++/WinRT statikus kiegészítő és/vagy mutációs függvény lesz. További információkért és egy példáért lásd: A konstruktor, a Current és a FEATURE_NAME átültetése.

A tagfüggvények esetében ismét el kell döntenie minden egyes függvénynél, hogy belekerüljön-e az IDL-be, vagy az implementációs típus nyilvános vagy privát tagfüggvénye legyen. További információkért és a döntési módra vonatkozó példákért tekintse meg a MainPage típus IDL-ját.

A XAML-jelölőnyelv és az erőforrásfájlok átvitele

Abban az esetben, ha a vágólap mintáját C++/WinRT-fájlba portoljuk C#-ból, ugyanazt az XAML-korrektúrát (beleértve az erőforrásokat) és az eszközfájlokat is használhattuk a C# és a C++/WinRT projektben. Bizonyos esetekben a korrektúra szerkesztése szükséges lesz ennek eléréséhez. Lásd: A MainPage portolásának befejezéséhez szükséges XAML és stílusok másolása.

A nyelven belüli eljárásokat érintő változások

Category C# C++/WinRT Lásd még
Élettartam-kezelés aszinkron metódusban N/A auto lifetime{ get_strong() }; vagy
auto lifetime = get_strong();
A CopyButton_Click metódus portolása
Kivezetés using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
A CopyImage metódus portolása
Objektum létrehozása new MyType(args) MyType{ args } vagy
MyType(args)
A Forgatókönyvek tulajdonság portolása
Nem inicializált referencia létrehozása MyType myObject; MyType myObject{ nullptr }; vagy
MyType myObject = nullptr;
A konstruktor, a Current és a FEATURE_NAME átültetése
Objektum létrehozása változóban argumentumokkal var myObject = new MyType(args); auto myObject{ MyType{ args } }; vagy
auto myObject{ MyType(args) }; vagy
auto myObject = MyType{ args }; vagy
auto myObject = MyType(args); vagy
MyType myObject{ args }; vagy
MyType myObject(args);
A Footer_Click metódus portolása
Objektum létrehozása változóban argumentumok nélkül var myObject = new T(); MyType myObject; A BuildClipboardFormatsOutputString metódus portolása
Objektum inicializálásának rövidítése var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Tömeges vektorművelet var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
A CopyButton_Click metódus portolása
Végigiterálni a gyűjteményen foreach (var v in c) for (auto&& v : c) A BuildClipboardFormatsOutputString metódus portolása
Kivétel elfogása catch (Exception ex) catch (winrt::hresult_error const& ex) A PasteButton_Click metódus portolása
Kivétel részletei ex.Message ex.message() A PasteButton_Click metódus portolása
Tulajdonságérték lekérése myObject.MyProperty myObject.MyProperty() A NotifyUser metódus portolása
Tulajdonságérték beállítása myObject.MyProperty = value; myObject.MyProperty(value);
Tulajdonságérték növelése myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Karakterláncok esetén használjon karakterlánc-összefűzőt
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Nyelvi karakterlánc – Windows-futtatókörnyezet-karakterlánc N/A winrt::hstring{ s }
Sztringépítés StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Sztringépítés
Sztring interpolációja $"{i++}) {s.Title}" winrt::to_hstring és/vagy winrt::hstring::operator+ Az OnNavigatedTo metódus portolása
Üres sztring összehasonlításhoz System.String.Empty winrt::hstring::empty Az UpdateStatus metódus portolása
Üres sztring létrehozása var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Szótári műveletek 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)
Típuskonvertálás (hiba esetén) (MyType)v v.as<MyType>() A Footer_Click metódus portolása
Típuskonvertálás (hiba esetén null érték) v as MyType v.try_as<MyType>() A PasteButton_Click metódus portolása
Az x:Name tulajdonsággal rendelkező XAML-elemek tulajdonságok MyNamedElement MyNamedElement() A konstruktor, a Current és a FEATURE_NAME átültetése
Váltás a felhasználói felületi szálra CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue vagy winrt::resume_foreground A NotifyUser metódus portolása és a HistoryAndRoaming metódus portolása
Felhasználói felületi elem felépítése imperatív kódban XAML-lapon Lásd : felhasználói felületi elem felépítése Lásd : felhasználói felületi elem felépítése

Az alábbi szakaszok részletesebben ismertetik a táblázat egyes elemeit.

Felhasználói felületi elem felépítése

Ezek a példakódok egy felhasználói felületi elem felépítését mutatják be egy XAML-oldal imperatív kódjában.

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()

A C#-típusok biztosítják az Object.ToString metódust .

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

A C++/WinRT közvetlenül nem biztosítja ezt a létesítményt, de alternatív megoldásokhoz fordulhat.

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

A C++/WinRT a winrt::to_hstring is támogatja korlátozott számú típus esetén. Túlterheléseket kell hozzáadnia minden további sztringbefűzni kívánt típushoz.

Nyelv Stringify int Felsorolás karakterlánccá alakítása
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);

Enum sztringelése esetén meg kell adnia a winrt::to_hstring implementációját.

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

Ezeket a karakterlánccá alakított értékeket az adatkötés gyakran implicit módon használja fel.

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

Ezek a kötések végrehajtják a kötött tulajdonság winrt::to_hstring értékét. A második példa (a StatusEnum) esetében a winrt::to_hstring saját túlterhelését kell megadnia, ellenkező esetben fordítóhiba jelenik meg.

Lásd még a Footer_Click metódus portolását.

Sztringépítés

Karakterláncok összeállításához a C# beépített StringBuilder típussal rendelkezik.

Category C# C++/WinRT
Sztringépítés StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Windows-futtatókörnyezet-karakterlánc hozzáfűzése a null karakterek megőrzésével builder.Append(s); builder << std::wstring_view{ s };
Új vonal hozzáadása builder.Append(Environment.NewLine); builder << std::endl;
Az eredmény elérése s = builder.ToString(); ws = builder.str();

Lásd még a BuildClipboardFormatsOutputString metódus portolását és a DisplayChangedFormats metódus portolását.

Kód futtatása a fő felhasználói felületi szálon

Ez a példa a vonalkódolvasó mintájából származik.

Ha egy C#-projekt fő felhasználói felületi szálán szeretne dolgozni, általában a DispatcherQueue.TryEnqueue metódust (vagy a régebbi CoreDispatcher.RunAsyncet használja az UWP-ben). Így néz ki a minta a C#-ban.

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

Sokkal egyszerűbb ezt a C++/WinRT-ben kifejezni. Figyelje meg, hogy a paramétereket érték szerint fogadjuk el, feltételezve, hogy az első felfüggesztési pont (ebben az esetben) co_awaitután szeretnénk elérni őket. További információ: Paraméterátadás.

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

Ha nem az alapértelmezett prioritáson kell elvégeznie a munkát, akkor tekintse meg a winrt::resume_foreground függvényt, amelynek túlterhelése prioritást vesz igénybe. A winrt::resume_foreground hívására való várakozást bemutató kódpéldákat lásd itt: A szálaffinitást szem előtt tartó programozás.

Definiálja a futtatókörnyezeti osztályait IDL-ben

Lásd a MainPage típus IDL-jét, és vond össze a .idl fájlokat.

Adja meg a szükséges C++/WinRT-Windows névtérfejlécfájlokat

A C++/WinRT-ben minden alkalommal, amikor Windows névterekből szeretne típust használni, a megfelelő C++/WinRT Windows névtérfejlécfájlt kell tartalmaznia. Példa: A NotifyUser metódus portolása.

Dobozolás és kicsomagolás

A C# automatikusan a skaláris elemeket objektumokká alakítja. A C++/WinRT megköveteli a winrt::box_value függvény explicit meghívását. Mindkét nyelvben expliciten kell kicsomagolni. Lásd: Boxolás és kicsomagolás a C++/WinRT használatával.

Az alábbi táblázatokban ezeket a definíciókat fogjuk használni.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# C++/WinRT
Ökölvívás o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Kicsomagolás i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

A C++/CX és a C# kivételt vált ki, ha nullértékű mutatót próbál értéktípussá kibontani. A C++/WinRT ezt programozási hibának tekinti, és összeomlik. A C++/WinRT-ben használja a winrt::unbox_value_or függvényt, ha azt az esetet szeretné kezelni, amelyben az objektum nem az ön által vélt típus.

Scenario C# C++/WinRT
Ismert egész szám kicsomagolása i = (int)o; i = unbox_value<int>(o);
Ha o null System.NullReferenceException Lezuhan
Ha o nem dobozos int System.InvalidCastException Lezuhan
Az int kicsomagolása, null érték esetén tartalékérték használata; minden más esetben összeomlás i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Ha lehetséges, csomagolja ki az intet; minden más esetben használjon tartalék megoldást i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Példa: Az OnNavigatedTo metódus portolása és a Footer_Click metódus portolása.

Sztring dobozolása és kicsomagolása

A sztringek valamilyen módon értéktípust, más módon pedig referenciatípust jelentek. A C# és a C++/WinRT eltérően kezeli a sztringeket.

Az ABI típusú HSTRING egy referenciaszámlált karakterláncra mutató mutató. De nem az IInspectable-ból származik, tehát technikailag nem objektum. Ezenkívül a null HSTRING az üres sztringet jelöli. Az IInspectable-ból nem származó dolgok dobozolása úgy történik, hogy egy IReference<T-be> burkolja őket, és a Windows-futtatókörnyezet egy standard implementációt biztosít a PropertyValue objektum formájában (az egyéni típusok tulajdonságtípusként vannak jelentve::OtherType).

A C# hivatkozástípusként Windows-futtatókörnyezet sztringet jelöl, míg a C++/WinRT értéktípusként egy sztringet. Ez azt jelenti, hogy egy becsomagolt null sztring attól függően különbözőképpen ábrázolható, hogy hogyan jutottunk el idáig.

Behavior C# C++/WinRT
Nyilatkozatok object o;
string s;
IInspectable o;
hstring s;
Sztringtípus kategóriája Hivatkozás típusa Érték típusa
null HSTRING formában jelenik meg "" hstring{}
Null és "" azonos? No Yes
Null érték érvényessége s = null;
s.Length NullReferenceException értéket ad meg
s = hstring{};
s.size() == 0 (érvényes)
Ha null sztringet rendel az objektumhoz o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Ha objektumhoz rendel "" o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Alapszintű ökölvívás és kicsomagolás.

Operation C# C++/WinRT
Karakterlánc becsomagolása o = s;
Az üres sztring nem null értékű objektummá válik.
o = box_value(s);
Az üres sztring nem null értékű objektummá válik.
Ismert karakterlánc kibontása s = (string)o;
A null objektum null sztring lesz.
InvalidCastException, ha az érték nem karakterlánc.
s = unbox_value<hstring>(o);
Null objektum összeomlik.
Összeomlik, ha nem karakterlánc.
Opcionális karakterlánc kibontása s = o as string;
A nullobjektum vagy a nem karakterlánc null karakterlánccá válik.

OR

s = o as string ?? fallback;
A null vagy nem karakterlánc típusú érték helyett az alapértelmezett érték lesz használva.
Üres sztring megőrzve.
s = unbox_value_or<hstring>(o, fallback);
A null vagy nem karakterlánc típusú érték helyett az alapértelmezett érték lesz használva.
Üres sztring megőrzve.

Osztály elérhetővé tétele a {Binding} korrektúrabővítmény számára

Ha a {Binding} korrektúrakiterjesztést az adattípushoz való adatkötéshez szeretné használni, tekintse meg a {Binding} használatával deklarált Kötés objektumot.

Objektumok használata XAML-jelölőnyelvből

Egy C#-projektben hozzáférhet a XAML-jelölőnyelv privát tagjaihoz és elnevezett elemeihez. A C++/WinRT rendszerben azonban az XAML {x:Bind} jelölőbővítménnyel felhasznált összes entitást nyilvánosan közzé kell tenni az IDL-ben.

Emellett a logikai értékhez való kötés C#-ban a true vagy false értéket jeleníti meg, míg C++/WinRT-ben a Windows.Foundation.IReference`1<Boolean> látható.

További információkért és kódpéldákért lásd Objektumok felhasználása jelölőnyelvből.

Adatforrás elérhetővé tétele a XAML-jelölőnyelv számára

A C++/WinRT 2.0.190530.8-s vagy újabb verziójában a winrt::single_threaded_observable_vector egy megfigyelhető vektort hoz létre, amely támogatja az IObservableVector<T> és az IObservableVector<IInspectable protokollt> is. Példaként lásd: a Forgatókönyvek tulajdonság portolása.

A MIDL-fájlt (.idl) így is megírhatja (lásd még: Futtatásidejű osztályok szétválasztása MIDL-fájlokba (.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; };
    }
}

És valósítsuk meg így.

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

További információ: XAML-elemek vezérlői; C++/WinRT-gyűjteményhez kötés, gyűjtemények C++/WinRT használatával.

Adatforrás elérhetővé tétele az XAML-korrektúra számára (a C++/WinRT 2.0.190530.8 előtt)

Az XAML-adatkötés megköveteli, hogy egy elemforrás megvalósítsa az IIterable<IInspectablet>, valamint az alábbi illesztőkombinációk egyikét.

  • IObservableVector<IInspectable>
  • IBindableVector és INotifyCollectionChanged
  • IBindableVector és IBindableObservableVector
  • IBindableVector önmagában (nem válaszol a változásokra)
  • IVector<IInspectable>
  • IBindableIterable (iterálja és menti az elemeket egy privát gyűjteménybe)

Egy általános felület, például az IVector<T> nem észlelhető futásidőben. Minden IVector<T> más interfészazonosítóval (IID) rendelkezik, amely a T függvénye. Bármely fejlesztő tetszőlegesen ki tudja bontani a T halmazt, így az XAML kötési kód egyértelműen soha nem tudja a teljes lekérdezési készletet. Ez a korlátozás nem jelent problémát a C# esetében, mert minden, az IEnumerable<T-t> implementáló CLR-objektum automatikusan implementálja az IEnumerable-t. Az ABI szintjén ez azt jelenti, hogy az IObservableVector<T-t> implementáló összes objektum automatikusan implementálja az IObservableVector<IInspectablet>.

A C++/WinRT nem biztosítja ezt a garanciát. Ha egy C++/WinRT futtatókörnyezeti osztály implementálja az IObservableVector<T-t>, akkor nem feltételezhetjük, hogy az IObservableVector<IInspectable> implementációja is meg van adva.

Következésképpen az előző példának így kell kinéznie.

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

És a megvalósítás.

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

Ha hozzá kell férnie az m_bookSkus-ban lévő objektumokhoz, akkor vissza kell QI-znia őket erre: 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;
}

Származtatott osztályok

A futtatókörnyezeti osztályból való levezetéshez az alaposztálynak összeállíthatónak kell lennie. A C# nem követeli meg, hogy bármilyen speciális lépést tegyen az osztályai komponálhatóvá tételéhez, a C++/WinRT azonban igen. Az unsealed kulcsszó arra szolgál, hogy jelezze, hogy az osztály alaposztályként használható.

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

A megvalósítási típus fejlécfájljában az alaposztály fejlécfájlját kell tartalmaznia, mielőtt belefoglalja a származtatott osztály automatikusan létrehozott fejlécét. Ellenkező esetben olyan hibaüzenetek jelennek meg, mint például "Az ilyen típusú kifejezések tiltott használata".

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

Fontos API-k