Einführung in Microsoft Interface Definition Language 3.0

Microsoft Interface Definition Language (MIDL) 3.0 ist eine vereinfachte, moderne Syntax zum Definieren Windows-Runtime Typen innerhalb von IDL-Dateien (Interface Definition Language) (.idlDateien). Diese neue Syntax wird jedem vertraut sein, der mit C, C++, C# und/oder Java erfahren hat. MIDL 3.0 ist eine besonders bequeme Möglichkeit zum Definieren von C++/WinRT-Laufzeitklassen , die deutlich präziser als frühere Versionen von IDL sind (die Reduzierung von Designs um zwei Drittel der Länge und die Verwendung angemessener Standardwerte, um die Notwendigkeit der Dekoration mit Attributen zu verringern).

Hier erfahren Sie, wie MIDL 3.0 aussieht; In diesem Beispiel werden die meisten Sprachsyntaxelemente veranschaulicht, die Sie wahrscheinlich verwenden.

// Photo.idl
namespace PhotoEditor
{
    delegate void RecognitionHandler(Boolean arg); // delegate type, for an event.

    runtimeclass Photo : Windows.UI.Xaml.Data.INotifyPropertyChanged // interface.
    {
        Photo(); // constructors.
        Photo(Windows.Storage.StorageFile imageFile);

        String ImageName{ get; }; // read-only property.
        Single SepiaIntensity; // read-write property.

        Windows.Foundation.IAsyncAction StartRecognitionAsync(); // (asynchronous) method.

        event RecognitionHandler ImageRecognized; // event.
    }
}

Beachten Sie, dass die Syntax von MIDL 3.0 speziell und ausschließlich für die Definition von Typen konzipiert ist. Sie verwenden eine andere Programmiersprache, um diese Typen zu implementieren . Um MIDL 3.0 zu verwenden, benötigen Sie Windows SDK Version 10.0.17134.0 (Windows 10, Version 1803) (midl.exe Version 8.01.0622 oder höher, die mit dem /winrt Switch verwendet wird).

Hinweis

Siehe auch den Windows-Runtime konsolidierten Verweis (Die Windows-Runtime Typsystem- und Windows-Metadatendateien).

MIDL 1.0, 2.0 und 3.0

Die Interface Definition Language (IDL) begann mit dem System "Distributed Computing Environment/Remote Procedure Calls(DCE/RPC)". Die ursprüngliche MIDL 1.0 ist DCE/RPC IDL mit Verbesserungen zum Definieren von COM-Schnittstellen und Coclasses.

Eine aktualisierte MIDL 2.0-Syntax (auch als MIDLRT bezeichnet) wurde dann in Microsoft entwickelt, um Windows-Runtime APIs für die Windows-Plattform zu deklarieren. Wenn Sie im Windows SDK-Ordner %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt suchen, werden Beispiele für .idl Dateien angezeigt, die mit der SYNTAX MIDL 2.0 geschrieben wurden. Diese sind integrierte Windows-Runtime-APIs, die in ihrem ABI-Formular (Application Binary Interface) deklariert sind. Diese Dateien sind in erster Linie für die Verwendung von Tools vorhanden– Sie erstellen diese APIs nicht in diesem Formular (es sei denn, Sie schreiben sehr code auf niedriger Ebene).

Siehe auch Übergang zu MIDL 3.0 von klassischem MIDLRT.

MIDL 3.0 ist eine viel einfachere und modernere Syntax, deren Zweck es ist, Windows-Runtime APIs zu deklarieren. Außerdem können Sie sie in Ihren Projekten verwenden, insbesondere zum Definieren von C++/WinRT-Laufzeitklassen . Die Header für die Verwendung von C++/WinRT für die integrierten Windows-Runtime-APIs sind Teil des SDK im Ordner%WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Anwendungsfälle für MIDL 3.0

Im Allgemeinen sind alle Windows-Runtime APIs für alle Windows-Runtime Sprachprojektionen verfügbar. Dies geschieht teilweise, indem Sie Windows-Runtime Typen ausschließlich an Windows-Runtime-APIs übergeben. Obwohl es sich um eine gültige Entwurfsentscheidung handelt, um eine unformatierte COM-Schnittstelle an und von einer Windows-Runtime-API zu übergeben, schränkt dies die Verbraucher dieser bestimmten Windows-Runtime-API auf C++-Anwendungen ein. Die Technik kann in Interoperationsszenarien angezeigt werden, z. B. beim Interoperieren zwischen Direct3D und XAML. Da Direct3D sich im Bild befindet, ist das Szenario notwendigerweise auf C++-Anwendungen beschränkt. Eine API, die eine COM-Schnittstelle erfordert, setzt also keine zusätzliche Einschränkung über das, was inhärent ist. Beispielsweise kann eine C++-Anwendung einen IDXGISwapChain-Schnittstellenzeiger abrufen und diese dann an die ISwapChainPanelNative::SetSwapChainChainChain-Methode übergeben. Eine C#-Anwendung kann beispielsweise keine IDXGISwapChain abrufen, damit diese Methode aus diesem Grund nicht verwendet werden kann. Diese interopbezogenen Ausnahmen leben in Interopheadern, z windows.ui.xaml.media.dxinterop.h. B. .

Wenn es Features oder Funktionen einer COM-Komponente gibt, die Sie Windows-Runtime Sprachprojektionen über C++ hinaus verfügbar machen möchten, können Sie eine C++-Windows-Runtime-Komponente (WRC) erstellen und verwenden, die die COM-Komponente (z. B. DirectX) direkt erstellt und verwendet, und stellt eine Replikation einiger Teilmenge seiner Features und Funktionen in Form einer Windows-Runtime API-Oberfläche, die nur Windows-Runtime Typen verwendet und zurückgibt. Sie könnten dann die WRC aus einer Anwendung nutzen, die in einer beliebigen Windows-Runtime Sprachprojektion geschrieben wurde.

Definitionsstruktur und Aufrufen von midl.exe aus der Befehlszeile

Die wichtigsten Organisationskonzepte in einer MIDL 3.0-Definition sind Namespaces, Typen und Member. Eine MIDL 3.0-Quelldatei (eine .idl Datei) enthält mindestens einen Namespace, in dem Es sich um Typen und/oder untergeordnete Namespaces handelt. Jeder Typ enthält null oder mehr Member.

  • Klassen, Schnittstellen, Strukturen und Enumerationen sind Typen.
  • Methoden, Eigenschaften, Ereignisse und Felder sind Beispiele für Elemente.

Wenn Sie eine MIDL 3.0-Quelldatei kompilieren, gibt der Compiler () eine Windows-Runtime Metadatendatei (midl.exein der Regel eine .winmd Datei) aus.

// Bookstore.idl
namespace Bookstore
{
    runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        BookSku();
        BookSku(Single price, String authorName, String coverImagePath, String title);

        Single Price;

        String AuthorName{ get; };
        Windows.UI.Xaml.Media.ImageSource CoverImage{ get; };
        String CoverImagePath{ get; };
        String Title{ get; };

        Boolean Equals(BookSku other);
        void ApplyDiscount(Single percentOff);
    }
}

Da der Namespace eines Windows-Runtime Typs Teil des Typnamens wird, definiert das obige Beispiel eine Laufzeitklasse namens "Bookstore.BookSku". Es gibt keine sprachunabhängige Methode zum Ausdruck von BookSku , ohne auch den Namespace auszudrücken.

Diese Klasse implementiert die Windows.UI.Xaml.Data.INotifyPropertyChanged-Schnittstelle . Die Klasse enthält mehrere Member: zwei Konstruktoren, eine Read-Write-Eigenschaft (Price), einige schreibgeschützte Eigenschaften (AuthorName über Titel) und zwei Methoden namens Equals und ApplyDiscount. Beachten Sie die Verwendung des Typs "Single " statt "Float". Und diese Zeichenfolge verfügt über einen Großbuchstaben "S".

Tipp

Visual Studio bietet die beste Oberfläche für die Kompilierung von MIDL 3.0 mithilfe der C++/WinRT Visual Studio Extension (VSIX). Siehe Visual Studio-Unterstützung für C++/WinRT und vsIX.

Sie können MIDL 3.0 aber auch über die Befehlszeile kompilieren. Wenn der Quellcode für dieses Beispiel in einer Datei mit dem Namen gespeichert Bookstore.idlist, können Sie den folgenden Befehl ausstellen. Falls erforderlich, können Sie die im Befehl verwendete SDK-Versionsnummer aktualisieren (was 10.0.17134.0 ist).

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" Bookstore.idl

Das midl.exe Tool kompiliert das Beispiel und erstellt eine Metadatendatei namens Bookstore.winmd (standardmäßig wird der Name der .idl Datei verwendet).

Tipp

Wenn Sie mehr als eine IDL-Datei verwenden (informationen dazu finden Sie unter Factoring-Laufzeitklassen in Midl-Dateien (IDL)), führen Sie alle resultierenden .winmd Dateien in einer einzelnen Datei mit demselben Namen wie dem Stammnamespace zusammen. Diese endgültige .winmd Datei ist die, auf die die Verbraucher Ihrer APIs verweisen.

In diesem Fall ist BookSku die einzige Laufzeitklasse im Bookstore-Namespace , daher haben wir einen Schritt gespeichert und nur die .idl Datei für den Namespace benannt.

Übrigens können Sie den where Befehl verwenden, um herauszufinden, wo midl.exe installiert ist.

where midl

Wenn Sie die in einer .idl Datei definierten Typen aus einer anderen .idl Datei verwenden möchten, verwenden Sie die import Direktive. Weitere Details und ein Codebeispiel finden Sie unter XAML-Steuerelemente; Binden an eine C++/WinRT-Eigenschaft. Wenn Sie natürlich eine integrierte oder Drittanbieterkomponente verwenden, haben Sie keinen Zugriff auf die .idl Datei. Beispielsweise können Sie die Win2D-Windows-Runtime-API für das Rendern von 2D-Grafiken im Unmittelbarmodus verwenden. Der obige Befehl verwendet den /reference Schalter, um auf eine Windows-Runtime Metadatendatei (.winmd) zu verweisen. In diesem nächsten Beispiel verwenden wir diesen Schalter erneut, indem wir uns das Szenario vorstellen, in dem wir haben Bookstore.winmd, aber nicht Bookstore.idl.

// MVVMApp.idl
namespace MVVMApp
{
    runtimeclass ViewModel
    {
        ViewModel();
        Bookstore.BookSku BookSku{ get; };
    }
}

Wenn der Quellcode für das obige Beispiel in einer Datei mit dem Namen gespeichert MVVMApp.idlist, können Sie den folgenden Befehl zur Referenz ausstellen Bookstore.winmd.

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" /reference Bookstore.winmd MVVMApp.idl

Namespaces

Ein Namespace ist erforderlich. Es präfixiert den Namen aller Typen, die im Bereich des Namespaceblocks mit dem Namespacenamen definiert sind. Ein Namespace kann auch untergeordnete Namespacedeklarationen enthalten. Der Name von Typen, die in einem untergeordneten Namespacebereich definiert sind, weisen ein Präfix aller enthaltenden Namespacenamen auf.

Die folgenden Beispiele sind zwei Methoden zum Deklarieren derselben Windows.Foundation.Uri-Klasse (wie Sie sehen können, trennen Zeiträume die Ebenen geschachtelter Namespaces).

namespace Windows.Foundation
{
    runtimeclass Uri : IStringable
    {
        ...
    }
}
namespace Windows
{
    namespace Foundation
    {
        runtimeclass Uri : IStringable
        {
            ...
        }
    }
}

Hier sehen Sie ein weiteres Beispiel, das zeigt, dass es legal ist, Namespaces und deren Typen in geschachtelter Weise zu deklarieren.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }

    namespace SubNs2
    {
        runtimeclass MySubNs2Class
        {
            void DoWork();
        }
    }
}

Es ist jedoch üblicher, den vorherigen Namespace zu schließen und ein neues zu öffnen, wie dies der Fall ist.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }
}

namespace RootNs.SubNs1.SubNs2
{
    runtimeclass MySubNs2Class
    {
        void DoWork();
    }
}

Typen

Es gibt zwei Arten von Datentypen in MIDL 3.0: Werttypen und Referenztypen. Eine Variable eines Werttyps enthält seine Daten direkt. Eine Variable eines Referenztyps speichert einen Verweis auf seine Daten (eine solche Variable wird auch als Objekt bezeichnet).

Es ist möglich, dass zwei Referenztypvariablen auf dasselbe Objekt verweisen. Daher wirkt sich ein Vorgang auf eine Variable auf das Objekt aus, auf das von der anderen Variable verwiesen wird. Bei Werttypen verfügen die Variablen jeweils über eine eigene Kopie der Daten, und es ist nicht möglich, dass sich ein Vorgang auf einen Vorgang auswirkt.

MIDL 3.0-Werttypen sind weiter in einfache Typen, Enumerationstypen, Strukturtypen und nullfähige Typen unterteilt.

MIDL 3.0-Referenztypen sind weiter in Klassentypen, Schnittstellentypen und Delegatentypen unterteilt.

Hier finden Sie eine Übersicht über das System des Typs MIDL 3.0. Im Gegensatz zu früheren Versionen von MIDL können Sie keine Aliase für diese Typen verwenden.

Category BESCHREIBUNG
Werttypen Einfache Typen Signiertes Integral: Int16, Int32, Int64
Nicht signiertes Integral: UInt8, UInt16, UInt32, UInt64
Unicode-Zeichen: Zeichen (stellt eine UTF-16LE dar; eine 16-Bit-Unicode-Codeeinheit)
Unicode-Zeichenfolgen: Zeichenfolge
IEEE-Gleitkommapunkt: Single, Double
Boolean: Boolean
128 Bit UUID: Guid
Enumerationstypen Benutzerdefinierte Typen der Formularenume E {...}
Strukturtypen Benutzerdefinierte Typen der Formularstruktur S {...}
Nullable-Typen Erweiterungen aller anderen Werttypen mit einem Nullwert
Verweistypen Klassentypen Ultimative Basisklasse aller anderen Typen: Objekt
Benutzerdefinierte Typen der Formular-Runtimeklasse C {...}
Schnittstellentypen Benutzerdefinierte Typen der Formularschnittstelle I {...}
Delegattypen Benutzerdefinierte Typen des Formularstellvertretungs <returnType> D(...)

Die sieben integralen Typen unterstützen 8-Bit-nicht signierte Daten; und 16-Bit-, 32-Bit- und 64-Bit-Werte in signierter oder nicht signierter Form.

Die beiden Gleitkommatypen, Singleund Double, stellen Daten mit der 32-Bit-Einzelgenauigkeit und 64-Bit-Double-Precision IEEE 754-Formaten dar.

MidL 3.0's Boolean-Typ stellt boolesche Werte dar; entweder true oder false.

Zeichen und Zeichenfolgen in MIDL 3.0 enthalten Unicode-Zeichen. Der Char-Typ stellt eine UTF-16LE-Codeeinheit dar; und der String-Typ stellt eine Sequenz von UTF-16LE-Codeeinheiten dar.

In der folgenden Tabelle werden die numerischen Typen von MIDL 3.0 zusammengefasst.

Category Bits type Bereich/Genauigkeit
Signiertes Integral 16 Int16 –32,768...32,767
32 Int32 –2,147,483,648...2,147,483,647
64 Int64 –9,223,372,036,854,775,808...9,223,372,036,854,775,807
Ganzzahlig ohne Vorzeichen 8 UInt8 0...255
16 UInt16 0...65,535
32 UInt32 0...4,294,967,295
64 UInt64 0...18,446,744,073,709,551,615
Gleitkomma 32 Single 1,5 × 10-45 bis 3,4 × 1038, 7-stellige Genauigkeit
64 Double 5.0 × 10-324 bis 1,7 × 10308, 15-stellige Genauigkeit

MIDL 3.0-Quelldateien verwenden Typdefinitionen, um neue Typen zu erstellen. Eine Typdefinition gibt den Namen und die Member des neuen Typs an. Diese MIDL 3.0-Typkategorien sind benutzerdefinierbar.

  • Attributtypen,
  • Strukturtypen,
  • Schnittstellentypen,
  • Laufzeitklassentypen,
  • Stellvertretungstypen und
  • Aufzählungstypen.

Ein Attributtyp definiert ein Windows-Runtime Attribut, das auf andere Typdefinitionen angewendet werden kann. Ein Attribut stellt Metadaten zum Typ bereit, auf den das Attribut angewendet wird.

Ein Strukturtyp definiert eine Windows-Runtime Struktur, die Datenelemente (Felder) enthält. Structs sind Werttypen, und sie erfordern keine Heap-Zuordnung. Ein Datenelement eines Strukturtyps muss entweder ein Werttyp oder ein nullabler Typ sein. Struct-Typen unterstützen keine Vererbung.

Ein Schnittstellentyp definiert eine Windows-Runtime Schnittstelle, die eine benannte Gruppe von Funktionsmitgliedern ist. Eine Schnittstelle kann angeben, dass eine Implementierung der Schnittstelle auch eine oder mehrere angegebene (erforderliche) Schnittstellen implementieren muss. Jeder Schnittstellentyp leitet direkt von der Windows-Runtime IInspectable-Schnittstelle ab.

Ein Runtimeclass-Typ definiert eine Windows-Runtime Klasse (Laufzeitklasse). Eine Laufzeitklasse enthält Elemente, die Eigenschaften, Methoden und Ereignisse sein können.

Ein Stellvertretungstyp definiert einen Windows-Runtime Delegaten, der einen Verweis auf eine Methode mit einer bestimmten Parameterliste und einem Rückgabetyp darstellt. Stellvertretungen ermöglichen es, eine Methode als Entität zu behandeln, die als Parameter übergeben werden kann. Ein Stellvertretung ähnelt dem Konzept eines Funktionszeigers, der in einigen anderen Sprachen gefunden wurde. Im Gegensatz zu Funktionszeigern sind Stellvertretungen objektorientiert und typsicher.

Ein Aufzählungstyp ist ein eindeutiger Typ mit benannten Konstanten. Jeder Aufzählungstyp weist einen impliziten zugrunde liegenden Typ auf; entweder Int32 oder UInt32. Der Satz von Werten eines Aufzählungstyps entspricht dem Satz von Werten des zugrunde liegenden Typs.

MIDL 3.0 unterstützt drei zusätzliche Typkategorien.

  • eindimensionale Arraytypen,
  • Nullwerttypen und
  • der Objekttyp .

Sie müssen kein eindimensionales Array deklarieren, bevor Sie es verwenden können. Stattdessen werden Arraytypen erstellt, indem hinter einen Typnamen eckige Klammern gesetzt werden. Beispielsweise ist Int32[] ein eindimensionales Array von Int32.

Ebenso müssen nullable Werttypen nicht definiert werden, bevor sie verwendet werden können. Für jeden nicht nullablen Werttyp T (außer String) gibt es einen entsprechenden nullablen Typ Windows.Foundation.IReference<T>, der den zusätzlichen Wert nullenthalten kann. Beispielsweise ist Windows.Foundation.IReference<Int32> ein Typ, der eine beliebige 32-Bit-Ganze Zahl oder den Wert nullenthalten kann. Siehe auch IReference<T>.

Schließlich unterstützt MIDL 3.0 den Objekttyp, der der Windows-Runtime IInspectable-Schnittstelle zugeordnet ist. Die Referenztypen der Schnittstelle und Laufzeitklasse werden vom Objekttyp abgeleitet; Stellvertretung nicht.

Ausdrücke in einem aufgezählten Wert

Mit MIDL 3.0 können Sie nur einen Ausdruck in der Definition des Werts eines benannten Typs verwenden; Mit anderen Worten, in einem Enumerations-Initializer.

Ein Ausdruck wird aus Operanden und Operatoren erstellt. Die Operatoren in einem Ausdruck geben an, welche Vorgänge auf die Operanden angewendet werden sollen. Beispiele für Operatoren sind +, -, *, /und new. Beispiele für Operanden sind Literale, Felder, lokale Variablen und Ausdrücke.

Wenn ein Ausdruck mehrere Operatoren enthält, steuert die Rangfolge der Operatoren die Reihenfolge, in der die einzelnen Operatoren ausgewertet werden. Beispielsweise wird der Ausdruck x + y * z als x + (y * z) ausgewertet, da der *-Operator höhere Priorität hat als der + Operator. Logische Vorgänge sind niedriger als bitweise Vorgänge.

In der folgenden Tabelle werden midL 3.0-Operatoren zusammengefasst, wobei die Operatorkategorien in der Reihenfolge der Rangfolge von der höchsten bis zur niedrigsten Rangfolge aufgelistet werden. Operatoren in derselben Kategorie haben die gleiche Rangfolge.

Kategorie Ausdruck BESCHREIBUNG
Primär x++ Postinkrement
x-- Postdekrement
Unäroperatoren +x Identity
-X Negation
!x Logische Negation
~x Bitweise Negation
++x Präinkrement
--x Prädekrement
Multiplikativ x * y Multiplikation
x / y Division
x % y Rest
Additiv x + y Addition, String-Verkettung, Stellvertretungskombination
x – y Subtraktion, Delegatentfernung
UMSCHALTTASTE x << y Linksverschiebung
x >> y Rechtsverschiebung
Bitweises AND x & y Ganze Zahl bitweise UND
Bitweises XOR x ^ y Ganzzahliger bitweiser XOR
Bitweises OR x | y Ganzzahlig ODER
Logisches AND x && y Boolescher logischer UND
Logisches OR x || y Boolescher logischer OR

Klassen

Klassen (oder Laufzeitklassen) sind die grundlegendsten MIDL 3.0-Typen. Eine Klasse ist eine Definition einer Aggregation von Methoden, Eigenschaften und Ereignissen in einer einzelnen Einheit. Klassen unterstützen Vererbung und Polymorphismus – Mechanismen, bei denen abgeleitete KlassenBasisklassen erweitern und spezialisieren können.

Sie definieren einen neuen Klassentyp mithilfe einer Klassendefinition. Eine Klassendefinition beginnt mit einem Header, der das runtimeclass Schlüsselwort, den Namen der Klasse, die Basisklasse (sofern angegeben) und die von der Klasse implementierten Schnittstellen angibt. Der Header folgt dem Klassentext, der aus einer Liste der Memberdeklarationen besteht, die zwischen den Trennzeichen { und }geschrieben wurden.

Hier ist eine Definition einer einfachen Klasse namens Area.

runtimeclass Area
{
    Area(Int32 width, Int32 height);

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

Dadurch wird eine neue Windows-Runtime Klasse namens Area definiert, die einen Konstruktor enthält, der zwei Int32-Parameter, zwei Int32-Lese-/Schreibeigenschaften namens "Height" und "Width" und eine statische schreibgeschützte Eigenschaft namens "NumberOfAreas" enthält.

Standardmäßig ist eine Laufzeitklasse versiegelt, und die Ableitung von dieser ist unzulässig. Siehe Basisklassen.

Um XAML an ein Ansichtsmodell zu binden, muss die Laufzeitklasse des Ansichtsmodells in MIDL definiert werden. Weitere Details finden Sie unter XAML-Steuerelemente; Binden an eine C++/WinRT-Eigenschaft.

Sie können deklarieren, dass eine Klasse keine Instanzen unterstützt (und daher nur statische Member enthalten muss), indem Sie die Laufzeitklassendefinition mit dem static Schlüsselwort präfixieren. Durch das Hinzufügen eines nicht statischen Elements zur Klasse wird dann ein Kompilierungsfehler verursacht.

static runtimeclass Area
{
    static Int32 NumberOfAreas { get; };
}

Eine statische Klasse unterscheidet sich von einer leeren Klasse. Siehe auch leere Klassen.

Sie können angeben, dass eine Klassendefinition unvollständig ist, indem Sie die Laufzeitklassendefinition mit dem partial Schlüsselwort präfixieren. Alle partiellen Klassendefinitionen, die vom Compiler gefunden werden, werden in eine einzelne Laufzeitklasse kombiniert. Dieses Feature ist in erster Linie für XAML-Erstellungsszenarien gedacht, in denen einige der partiellen Klassen computergeneriert werden.

Modifizierer Bedeutung
Statisch Die Klasse weist keine Instanzen auf. Daher sind nur statische Elemente zulässig.
partial Die Klassendefinition ist unvollständig.

Siehe Komposition und Aktivierung für erweiterte Modifizierer.

Memberzugriffsmodifizierer

Da MIDL 3.0 eine Definitionssprache für die Beschreibung der öffentlichen Oberfläche von Windows-Runtime Typen ist, ist keine explizite Syntax erforderlich, um die öffentliche Barrierefreiheit eines Elements zu deklarieren. Alle Mitglieder sind implizit öffentlich. Deshalb erfordert MIDL 3.0 nicht das (effektiv redundante) public Schlüsselwort.

Basisklassen

Eine Klassendefinition kann eine Basisklasse angeben, indem Sie den Klassennamen und Typparametern mit einem Doppelpunkt und dem Namen der Basisklasse folgen. Das Auslassen einer Basisklassenspezifikation entspricht der Ableitung vom Typ Object (also von IInspectable).

Hinweis

Ihre Ansichtsmodellklassen – tatsächlich – jede Laufzeitklasse, die Sie in Ihrer Anwendung definieren – müssen nicht von einer Basisklasse abgeleitet werden.

Jede Laufzeitklasse, die Sie in der Anwendung definieren, die von einer Basisklasse abgeleitet wird, wird als kompposierbare Klasse bezeichnet. Für zusammensetzbare Klassen gelten bestimmte Einschränkungen. Damit eine Anwendung die Tests des Zertifizierungskits für Windows-Apps besteht, das von Visual Studio sowie vom Microsoft Store zur Überprüfung von Übermittlungen verwendet wird, und erfolgreich in den Microsoft Store aufgenommen werden kann, muss eine zusammensetzbare Klasse letztendlich von einer Windows-Basisklasse abgeleitet sein. Das bedeutet, dass es sich am Stamm der Vererbungshierarchie um einen Klassentyp aus einem Windows.*-Namespace handeln muss.

Weitere Details finden Sie unter XAML-Steuerelemente; Binden an eine C++/WinRT-Eigenschaft.

Im nächsten Beispiel ist die Basisklasse "Volume" "Area" und die Basisklasse "Area" ist "Windows.UI.Xaml.DependencyObject".

unsealed runtimeclass Area : Windows.UI.Xaml.DependencyObject
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

Hinweis

Hier werden Bereich und Volume in derselben Quelldatei definiert. Eine Diskussion der Experten und Nachteile finden Sie unter Factoring-Laufzeitklassen in Midl-Dateien (IDL).idl).

Eine Klasse erbt die Member der zugehörigen Basisklasse. Vererbung bedeutet, dass eine Klasse implizit alle Member der Basisklasse enthält, mit Ausnahme der Konstruktoren der Basisklasse. Eine abgeleitete Klasse kann den geerbten Membern neue Member hinzufügen, aber die Definition eines geerbten Members kann nicht entfernt werden.

Im vorherigen Beispiel erbt Volume die Eigenschaften "Height " und "Width " von "Area". Daher enthält jede Volumeinstanz drei Eigenschaften: Höhe, Breite und Tiefe.

Im Allgemeinen erfordern Typauflösungsregeln, dass ein Typname vollständig qualifiziert ist, wenn darauf verwiesen wird. Eine Ausnahme besteht darin, dass der Typ im selben Namespace wie der aktuelle Typ definiert wurde. Das obige Beispiel funktioniert wie geschrieben, wenn "Area " und "Volume " beide im gleichen Namespace enthalten sind.

Implementierte Schnittstellen

Eine Klassendefinition kann auch eine Liste der Schnittstellen angeben, die die Klasse implementiert. Sie geben die Schnittstellen als durch Trennzeichen getrennte Liste der Schnittstellen nach der (optionalen) Basisklasse an.

Im folgenden Beispiel implementiert die Area-Klasse die IStringable-Schnittstelle ; und die Volume-Klasse implementiert sowohl IStringable als auch die hypothetische IEquatable-Schnittstelle .

unsealed runtimeclass Area : Windows.Foundation.IStringable
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area, Windows.Foundation.IStringable, IEquatable
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

In der MIDL deklarieren Sie die Elemente der Schnittstelle nicht für die Klasse. Sie müssen sie natürlich deklarieren und definieren, um sie für die tatsächliche Implementierung zu definieren.

Members

Die Member einer Klasse sind entweder statische Member oder Instanzmember. Ein statisches Element gehört zu einer Klasse. Ein Instanzelement gehört zu einem Objekt (also einer Instanz einer Klasse).

Diese Tabelle zeigt die Arten von Membern, die eine Klasse enthalten kann.

Mitgliedertyp BESCHREIBUNG
Konstruktoren Aktionen, die erforderlich sind, um eine Instanz der Klasse zu initialisieren oder die Klasse selbst zu initialisieren
Eigenschaften Aktionen, die dem Lesen und Schreiben benannter Eigenschaften einer Instanz der Klasse oder der Klasse selbst zugeordnet sind
Methoden Berechnungen und Aktionen, die von einer Instanz der Klasse oder von der Klasse selbst ausgeführt werden können
Ereignisse Benachrichtigungen, die von einer Instanz der Klasse ausgelöst werden können

Konstruktoren

MIDL 3.0 unterstützt die Deklaration von Instanzkonstruktoren. Ein Instanzkonstruktor ist eine Methode, die die erforderlichen Aktionen implementiert, um eine Instanz einer Klasse zu initialisieren. Konstruktoren sind möglicherweise nicht statisch.

Ein Konstruktor wird wie eine Instanzmethode (aber ohne Rückgabetyp) deklariert und mit demselben Namen wie die enthaltende Klasse.

Instanzkonstruktoren können überladen werden. Die nachstehende Testklasse deklariert beispielsweise drei Instanzkonstruktoren; eine ohne Parameter (der Standardkonstruktor ), eine, die einen Int32-Parameter verwendet, und eine, die zwei Double-Parameter (parametrisierte Konstruktoren) verwendet.

runtimeclass Test
{
    Test();
    Test(Int32 x);
    Test(Double x, Double y);
}

Ausführliche Informationen zur Syntax für Parameterlisten finden Sie unter "Methoden unten".

Instanzeigenschaften, Methoden und Ereignisse werden geerbt. Instanzenkonstruktoren werden nicht geerbt (mit einer Ausnahme), und eine Klasse verfügt über keine Instanzenkonstruktoren, die nicht in der Klasse deklariert sind. Wenn kein Instanzkonstruktor für eine Klasse bereitgestellt wird, können Sie die Klasse nicht direkt instanziieren. Für eine solche Klasse verfügen Sie normalerweise über eine Factorymethode, die eine Instanz der Klasse zurückgibt.

Die Ausnahme ist nicht versiegelte Klassen. Eine nicht versiegelte Klasse kann über einen oder mehrere geschützte Konstruktoren verfügen.

Eigenschaften

Eigenschaften sind konzeptual ähnlich wie Felder (z. B. C#-Felder oder felder einer MIDL 3.0-Struktur). Beide Eigenschaften und Felder sind Elemente mit einem Namen und einem zugeordneten Typ. Im Gegensatz zu Feldern geben Eigenschaften jedoch keine Speicherorte an. Stattdessen verfügen Eigenschaften über Accessoren , die angeben, welche Funktion ausgeführt werden soll, wenn Sie eine Eigenschaft lesen oder schreiben.

Eine Eigenschaft wird wie das Feld einer Struktur deklariert, außer dass die Deklaration mit einem get Schlüsselwort und/oder einem set Schlüsselwort endet, das zwischen den Trennzeichen { und }geschrieben wurde und in einem Semikolon endet.

Eine Eigenschaft, die sowohl ein get Schlüsselwort als auch ein set Schlüsselwort enthält, ist eine Lese-/Schreibzugriffseigenschaft. Eine Eigenschaft, die nur über ein get Schlüsselwort verfügt, ist eine schreibgeschützte Eigenschaft. Die Windows-Runtime unterstützt keine schreibgeschützten Eigenschaften.

Der Zuvor gesehene Klassenbereich enthält beispielsweise zwei Lese-/Schreibzugriffseigenschaften namens Height und Width.

unsealed runtimeclass Area
{
    Int32 Height { get; set; };
    Int32 Width; // get and set are implied if both are omitted.
}

Die Deklaration der Breite lässt die Geschweifte und die get Schlüsselwörter set aus. Die Auslassung impliziert, dass die Eigenschaft schreibgeschützt ist und semantisch identisch ist mit der Bereitstellung der get und set Schlüsselwörter in dieser Reihenfolge –get gefolgt von set.

Darüber hinaus können Sie nur das get Schlüsselwort angeben, um anzugeben, dass die Eigenschaft schreibgeschützt ist.

// Read-only instance property returning mutable collection.
Windows.Foundation.Collections.IVector<Windows.UI.Color> Colors { get; };

Die Windows-Runtime unterstützt keine schreibgeschützten Eigenschaften. Sie können jedoch nur das set Schlüsselwort angeben, um eine vorhandene schreibgeschützte Eigenschaft in eine Lese-/Schreibeigenschaft zu überarbeiten. Verwenden Sie diese Version von Area als Beispiel.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
}

Wenn Sie anschließend die SurfaceColor-Eigenschaft schreibgeschützt machen möchten, und Sie müssen die binäre Kompatibilität mit früheren Definitionen von Area nicht beibehalten (z. B. ist die Area-Klasse ein Typ in einer Anwendung, die Sie jedes Mal neu kompilieren), dann können Sie einfach das set Schlüsselwort zur vorhandenen SurfaceColor-Deklaration wie dieser hinzufügen.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; set; };
}

Wenn Sie dagegen binäre Stabilität benötigen (z. B. die Area-Klasse ist eine Komponente in einer Bibliothek, die Sie an Kunden senden), können Sie das set Schlüsselwort nicht zur vorhandenen Eigenschaftsdeklaration hinzufügen. Dadurch wird die binäre Schnittstelle in Ihre Klasse geändert.

Fügen Sie in diesem Fall das Eigenschaftswort set zu einer zusätzlichen Definition der Eigenschaft am Ende der Klasse wie folgt hinzu.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
    ...
    Color SurfaceColor { set; };
}

Der Compiler erzeugt einen Fehler für eine schreibgeschützte Eigenschaft. Aber das ist nicht das, was hier getan wird. Aufgrund der vorherigen Deklaration der Eigenschaft als schreibgeschützt deklariert das Hinzufügen des Set-Schlüsselworts keine schreibgeschützte Eigenschaft, sondern eine Schreibzugriffseigenschaft.

Die Windows-Runtime Implementierung einer Eigenschaft ist eine oder zwei Accessormethoden auf einer Schnittstelle. Die Reihenfolge der Abrufen- und Festlegen von Schlüsselwörtern in der Eigenschaftsdeklaration bestimmt die Reihenfolge der Get- und Set-Accessormethoden in der Sicherungsschnittstelle.

Der get Accessor entspricht einer parameterlosen Methode mit einem Rückgabewert des Eigenschaftstyps – dem Eigenschafts-Getter.

Ein set Accessor entspricht einer Methode mit einem einzelnen Parameter namens "Value" und ohne Rückgabetyp – dem Eigenschaftssatzer.

Folglich erzeugen diese beiden Deklarationen unterschiedliche binäre Schnittstellen.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Statische und Instanzeigenschaften

Ähnlich wie methoden unterstützt MIDL 3.0 sowohl Instanzeigenschaften als auch statische Eigenschaften. Statische Eigenschaften werden mit dem static Präfix des Modifizierers deklariert, und Instanzeigenschaften werden ohne ihn deklariert.

Methoden

Eine Methode ist ein Element, das eine Berechnung oder Aktion implementiert, die von einer Instanz der Klasse oder von der Klasse selbst ausgeführt werden kann. Auf eine statische Methode wird über die Klasse zugegriffen. Auf eine Instanzmethode wird über eine Instanz der Klasse zugegriffen.

Eine Methode verfügt über eine (möglicherweise leere) Liste von Parametern, die Werte oder Variablenverweise darstellen, die an die Methode übergeben werden. Eine Methode verfügt auch über einen Rückgabetyp, der den Typ des berechneten und von der Methode zurückgegebenen Werts angibt. Der Rückgabetyp einer Methode ist void , wenn kein Wert zurückgegeben wird.

// Instance method with no return value.
void AddData(String data);

// Instance method *with* a return value.
Int32 GetDataSize();

// Instance method accepting/returning a runtime class.
// Notice that you don't say "&" nor "*" for reference types.
BasicClass MergeWith(BasicClass other);

// Asynchronous instance methods.
Windows.Foundation.IAsyncAction UpdateAsync();
Windows.Foundation.IAsyncOperation<Boolean> TrySaveAsync();

// Instance method that returns a value through a parameter.
Boolean TryParseInt16(String input, out Int16 value);

// Instance method that receives a reference to a value type.
Double CalculateArea(ref const Windows.Foundation.Rect value);

// Instance method accepting or returning a conformant array.
void SetBytes(UInt8[] bytes);
UInt8[] GetBytes();

// instance method that writes to a caller-provided conformant array
void ReadBytes(ref UInt8[] bytes);

Die Signatur einer Methode muss innerhalb der Klasse eindeutig sein, in der die Methode deklariert ist. Die Signatur einer Methode besteht aus dem Namen der Methode, den Typen seiner Parameter und/oder der Anzahl seiner Parameter. Die Signatur einer Methode umfasst nicht den Rückgabetyp.

Methodensichtsmodifizierer

Eine Methode verfügt möglicherweise über einen von zwei optionalen Sichtbarkeitsmodifizierern, wenn die Methode in einer abgeleiteten Klasse vorhanden ist.

Der überschreibende Modifizierer gibt an, dass diese Methode möglicherweise von einer Methode (mit demselben Namen und derselben Signatur) außer Kraft gesetzt werden kann, die zu einer Unterklasse gehört.

Der geschützte Modifizierer gibt an, dass diese Methode nur von Mitgliedern in einer nachfolgenden abgeleiteten Klasse zugänglich ist.

Methodenüberladung

Die Methodenüberladung ermöglicht es mehreren Methoden in derselben Klasse, denselben Namen zu haben, solange sich ihre Parameter in der Zahl unterscheiden (mit anderen Worten, die Methoden weisen unterschiedliche Arität auf).

runtimeclass Test
{
    static void F();
    static void F(Double x);
    static void F(Double x, Double y);
}

Hinweis

Alle Methoden mit demselben Namen sollten unterschiedlich sein. Das liegt daran, dass schwach typierte Programmiersprachen keine Überladung nach Typ unterstützen.

Parameter

Parameter werden verwendet, um Werte oder Variableverweise an eine Methode zu übergeben. Ein Parameter beschreibt einen Slot mit einem Typ und einem Namen und optional einige Modifiziererschlüsselworte. Ein Argument ist ein tatsächlicher Wert, der in diesem Steckplatz vom Aufrufer der Methode an den Angerufenen übergeben wird.

Parameter einer Methode erhalten ihren Wert aus dem spezifischen Argument , das angegeben wird, wenn die Methode aufgerufen wird. Die Art und Weise, wie Argumente zwischen Anrufer und Aufrufer übergeben werden, hängt vom Typ des Parameters ab. Standardmäßig sind alle Parameter Eingabeparameter, das heißt, sie werden nur vom Aufrufer an den Angerufenen gepostet. Die Modifier-Schlüsselwörter ref, ref constund out können hinzugefügt werden, um die Standardrichtung des Marshalings zwischen Anrufer und Aufrufer zu ändern und Ausgabeparameter zu erstellen. Nicht alle Schlüsselwörter sind mit allen Parametertypen gültig; gültige Kombinationen sind unten aufgeführt.

Wichtig

Die Common Language Runtime (CLR) verfügt über Konzepte und Modifier-Schlüsselwörter, die möglicherweise ähnlich wie in diesem Abschnitt beschrieben sind. In der Praxis sind dies jedoch nicht verbunden, und die Wirkung dieser Modifizierer ist speziell für das Design und die Funktionsweise der Windows-Runtime.

Werttypen sind implizit Eingabeparameter, und standardmäßig wird eine Kopie des Arguments vom Aufrufer an den Angerufenen übergeben. Wertparameter können mit dem out Schlüsselwort in Ausgabeparameter transformiert werden. In diesem Fall wird das Argument stattdessen vom Aufrufer zurück zum Aufrufer gepostet.

runtimeclass Test
{
    static void Divide(Int32 x, Int32 y, out Int32 result, out Int32 remainder);
}

Als spezielle Leistungsoptimierung können Strukturtypen (und kein anderer Typ), die normalerweise als vollständige Kopie übergeben werden, durch Zeiger an die unveränderliche Struktur übergeben werden. Dies wird mit dem ref const Schlüsselwort (nichtconst ref) erreicht, das den Strukturparameter als Eingabeparameter markiert, aber den Marshaler angibt, einen Zeiger an den Speicher der Struktur zu übergeben, anstatt eine vollständige Kopie der Struktur zu übergeben. Beachten Sie jedoch, dass die Struktur unveränderlich ist; der Zeiger ist konzeptionell ein Konstzeiger. Es gibt keine Boxen. Dies ist eine praktische Wahl, wenn sie einen Wert so groß wie eine Matrix4x4 akzeptiert, beispielsweise.

runtimeclass Test
{
    static Boolean IsIdentity(ref const Windows.Foundation.Numerics.Matrix4x4 m);
}

Referenztypen sind auch implizit Eingabeparameter, was bedeutet, dass der Aufrufer für die Zuordnung des Objekts verantwortlich ist und einen Verweis als Argument übergeben wird; Da das Argument jedoch ein Verweis auf das Objekt ist, werden Änderungen an diesem Objekt durch den aufgerufenen Aufrufer nach dem Aufruf beobachtet. Alternativ kann ein Verweistyp mit dem out Schlüsselwort einen Ausgabeparameter erstellt werden. In diesem Fall werden die Rollen umgekehrt; der Aufruf ist der, der das Objekt angibt und ihn zurück an den Aufrufer zurückgibt. Erneut können die ref Schlüsselwörter im Allgemeinen nicht mit Referenztypen verwendet werden (siehe Ausnahme unten).

runtimeclass Test
{
    static void CreateObjectWithConfig(Config config, out MyClass newObject);
}

In der folgenden Tabelle wird das Verhalten der Marshaling-Schlüsselwörter für Wertparameter und Referenzparameter zusammengefasst:

Verhalten Zugewiesen durch Stichwort Typen Bemerkungen
Eingabeparameter Caller (none) Alle Typen Standardverhalten
ref const Nur Struktur Leistungsoptimierung
Ausgabeparameter Aufgerufener out Alle Typen

Windows-Runtime unterstützt Arraytypen, deren Verhalten als Parameter etwas anders ist. Ein Array ist eine Datenstruktur, die eine Reihe von Variablen enthält, die sequenziell gespeichert und über einen Index zugegriffen werden. Die Variablen, die in einem Array enthalten sind, auch als Elemente des Arrays bezeichnet, sind alle vom gleichen Typ, und dieser Typ wird als Elementtyp des Arrays bezeichnet.

MIDL 3.0 unterstützt Deklarationen eines einzeldimensionalen Arrays.

Ein Arrayparameter ist ein Referenztyp und wie alle Referenztypen standardmäßig ein Eingabeparameter. In diesem Fall weist der Aufrufer das Array dem angerufenen Array zu, das seine Elemente lesen kann, sie aber nicht ändern kann (schreibgeschützt). Dies wird als Passarraymuster bezeichnet. Alternativ kann das Füllarraymuster verwendet werden, indem sie dem Parameter das ref Schlüsselwort hinzufügt; in diesem Setup wird das Array weiterhin vom Aufrufer zugewiesen, ist jedoch konzeptioneller Art und Weise ein Ausgabeparameter im Sinne, dass die angerufenen Elemente die Werte der Arrayelemente füllen. Schließlich ist das letzte Muster das Empfangenarray , bei dem (wie alle Ausgabereferenzparameter) der Aufrufer sowohl das Argument zuordnen als auch initialisieren, bevor er an den Aufrufer zurückgegeben wird.

runtimeclass Test
{
    // Pass array pattern: read-only array from caller to callee
    void PassArray(Int32[] values);

    // Fill array pattern: caller allocates array for callee to fill
    void FillArray(ref Int32[] values);

    // Receive array pattern: callee allocates and fill an array returned to caller
    void ReceiveArray(out Int32[] values);
}

In der folgenden Tabelle werden das Verhalten für Arrays und deren Elemente zusammengefasst:

Arraymuster Stichwort Zugewiesen durch Elementezugriff durch aufrufen
"Pass array" (none) Caller Schreibgeschützt
"Füllarray" ref Caller Lesegeschützt
"Array empfangen" out Aufgerufener Lese-/Schreibzugriff

Weitere Informationen zur Verwendung von C-Style-Arrayparametern – auch als konforme Arrays bezeichnet – mit C++/WinRT finden Sie unter Arrayparameter.

Statische Methoden und Instanzmethoden

Eine mit einem static Präfix deklarierte Methode ist eine statische Methode. Eine statische Methode hat keinen Zugriff auf eine bestimmte Instanz und kann daher nur direkt auf andere statische Elemente der Klasse zugreifen.

Eine Methode, die ohne einen static-Modifizierer deklariert wird, ist eine Instanzmethode. Eine Instanzmethode hat Zugriff auf eine bestimmte Instanz und kann sowohl auf statische als auch auf Instanzmitglieder der Klasse zugreifen.

Die folgende Entitätsklasse verfügt sowohl über statische als auch über Instanzenelemente.

runtimeclass Entity
{
    Int32 SerialNo { get; };
    static Int32 GetNextSerialNo();
    static void SetNextSerialNo(Int32 value);
}

Jede Entitätsinstanz enthält eine eigene Seriennummer (und vermutlich einige andere Informationen, die hier nicht angezeigt werden). Intern initialisiert der Entitätskonstruktor (wie eine Instanzmethode) die neue Instanz mit der nächsten verfügbaren Seriennummer.

Die SerialNo-Eigenschaft bietet Zugriff auf die Seriennummer für die Instanz, auf der Sie die Eigenschaft get-Methode aufrufen.

Die Methoden "GetNextSerialNo " und "SetNextSerialNo" können auf das interne nächste verfügbare seriennummernstatische Element der Entitätsklasse zugreifen.

Überschreibende und geschützte Methoden

Alle Methoden in einem Windows-Runtime Typ sind effektiv virtual. Beim Aufruf einer virtuellen Methode bestimmt der Laufzeittyp der Instanz, für die der Aufruf erfolgt, die tatsächlich aufzurufende Methodenimplementierung.

Eine Methode kann in einer abgeleiteten Klasse außer Kraft gesetzt werden. Wenn eine Instanzmethodedeklaration einen overridable Modifier enthält, kann die Methode von abgeleiteten Klassen außer Kraft gesetzt werden. Ob eine abgeleitete Klasse tatsächlich eine überschriebene Basisklassemethode außer Kraft setzt, wird durch die Implementierung bestimmt; es ist nicht in den Metadaten vorhanden. Wenn eine abgeleitete Klasse eine Methode in der Basisklasse neu deklariert, deklariert sie eine neue Methode, die sich neben der abgeleiteten Klassenmethode befindet, anstatt sie außer Kraft zu setzen.

Wenn eine Instanzmethodedeklaration einen protected Modifizierer enthält, ist die Methode nur für abgeleitete Klassen sichtbar.

Ereignisse

Eine Ereignisdeklaration ist ein Element, das angibt, dass eine Klasse eine Ereignisquelle ist. Eine solche Ereignisquelle stellt Benachrichtigungen für jeden Empfänger bereit, der eine Stellvertretung implementiert (eine Methode mit einer bestimmten Signatur).

Sie deklarieren ein Ereignis mithilfe event des Schlüsselworts, gefolgt vom Stellvertretungstypnamen (der die erforderliche Methodensignatur beschreibt), gefolgt vom Namen des Ereignisses. Nachfolgend finden Sie ein Beispielereignis, das einen vorhandenen Stellvertretungstyp aus der Plattform verwendet.

runtimeclass Area
{
    ...
    event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
    ...
}

Eine Ereignisdeklaration fügt implizit zwei Methoden zur Klasse hinzu: eine Add-Methode , die ein Client aufruft, um der Quelle einen Ereignishandler hinzuzufügen, und eine Remove-Methode , die ein Client aufruft, um einen zuvor hinzugefügten Ereignishandler zu entfernen. Hier sind weitere Beispiele.

// Instance event with no meaningful payload.
event Windows.Foundation.TypedEventHandler<BasicClass, Object> Changed;

// Instance event with event parameters.
event Windows.Foundation.TypedEventHandler<BasicClass, BasicClassSaveCompletedEventArgs> SaveCompleted;

// Static event with no meaningful payload.
static event Windows.Foundation.EventHandler<Object> ResetOccurred;

// Static event with event parameters.
static event Windows.Foundation.EventHandler<BasicClassDeviceAddedEventArgs> DeviceAdded;

In der Konvention werden immer zwei Parameter an einen Windows-Runtime Ereignishandler übergeben: die Identität des Absenders und ein Ereignisargumentobjekt. Der Absender ist das Objekt, das das Ereignis ausgelöst hat oder null für statische Ereignisse. Wenn das Ereignis keine aussagekräftige Nutzlast aufweist, ist die Ereignisargumente ein Objekt , dessen Wert null ist.

Delegaten

Ein Stellvertretungstyp gibt eine Methode mit einer bestimmten Parameterliste und einem Rückgabetyp an. Eine einzelne Instanz eines Ereignisses kann eine beliebige Anzahl von Verweisen auf Instanzen des Stellvertretungstyps enthalten. Die Deklaration ähnelt der einer regulären Membermethode, außer dass es außerhalb einer Laufzeitklasse vorhanden ist und mit dem delegate Schlüsselwort präfixiert ist.

Ein Stellvertretung ermöglicht es, Methoden als Entitäten zu behandeln, die Variablen zugewiesen und als Parameter übergeben werden können. Delegaten ähneln konzeptionell Funktionszeigern, die es in einigen anderen Sprachen gibt. Im Gegensatz zu Funktionszeigern sind Stellvertretungen objektorientierte und typsicher.

Wenn wir den WindowSizeChangedEventHandler-Stellvertretungstyp nicht aus der Plattform verwenden möchten, können wir unseren eigenen Stellvertretungstyp definieren.

delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);

Eine Instanz unseres SizeChangedHandler-Stellvertretungstyps kann auf eine beliebige Methode verweisen, die zwei Argumente verwendet (ein Objekt und ein WindowSizeChangedEventArgs), und gibt leer zurück. Nachdem wir die Anweisungen erläutert haben, können Sie den Parameter WindowSizeChangedEventArgs auch durch einen eigenen Ereignistyp ersetzen.

Eine interessante und nützliche Eigenschaft eines Stellvertretungsvertreters besteht darin, dass sie nicht über die Klasse der Methode weiß oder kümmert, auf die sie verweist; alles ist wichtig, dass die referenzierte Methode dieselben Parameter aufweist und den Rückgabetyp als Stellvertretung zurückgibt.

Sie können optional eine Stellvertretungsdeklaration mit [uuid(...)].

Siehe auch Stellvertretungen, die HRESULT zurückgeben.

Strukturen

Eine Struktur ist eine Datenstruktur, die Datenelemente (Felder) enthalten kann. Im Gegensatz zu einer Klasse ist eine Struktur jedoch ein Werttyp.

Strukturen sind besonders nützlich für kleine Datenstrukturen, die über Wertsemantik verfügen. Komplexe Zahlen oder Punkte in einem Koordinatensystem sind gute Beispiele für Strukturen. Die Verwendung von Strukturen anstelle von Klassen für kleine Datenstrukturen kann einen großen Unterschied in der Anzahl der Speicherzuweisungen machen, die eine Anwendung ausführt.

Verwenden wir ein Beispiel, um Klassen und Strukturen zu kontrastieren. Hier ist eine Version von Point zuerst als Klasse.

runtimeclass Point
{
    Point(Int32 x, Int32 y);
    Int32 x;
    Int32 y;
}

Dieses C#-Programm erstellt und initialisiert ein Array von 100 Instanzen von Point. Bei der Implementierung von Point als Klasse werden separate Objekte 101 instanziiert: eine für das Arrayobjekt selbst; und eine für jede der 100 Punkt-Elemente .

class Test
{
    static Test()
    {
        Point[] points = new Point[100];
        for (Int32 i = 0; i < 100; ++i) points[i] = new Point(i, i);
    }
}

Eine mehr performante Alternative besteht darin, point astruct anstelle einer Klasse zu erstellen.

struct Point
{
    Int32 x;
    Int32 y;
};

Jetzt wird nur ein Objekt instanziiert – das Arrayobjekt selbst. Die Punktelemente werden innerhalb des Arrays in Zeile gespeichert; eine Speicheranordnung, die Prozessorcaches verwenden können, um leistungsstarke Wirkung zu erzielen.

Das Ändern einer Struktur ist eine binäre Änderung. Daher werden die im Rahmen von Windows selbst implementierten Strukturen nicht geändert, sobald sie eingeführt wurden.

Schnittstellen

Eine Schnittstelle definiert einen Vertrag, der von Klassen implementiert werden kann. Eine Schnittstelle kann Methoden, Eigenschaften und Ereignisse enthalten – genau wie Klassen.

Im Gegensatz zu einer Klasse bietet eine Schnittstelle keine Implementierungen der Elemente, die sie definiert. Es gibt lediglich die Elemente an, die von jeder Klasse bereitgestellt werden müssen, die die Schnittstelle implementiert.

Schnittstellen erfordern möglicherweise eine Klasse, die die Schnittstelle implementiert, um auch andere Schnittstellen zu implementieren. Im folgenden Beispiel erfordert die Schnittstelle IComboBox , dass jede Klasse, die IComboBox implementiert, auch ITextBox und IListBox implementiert. Darüber hinaus muss eine Klasse, die IComboBox implementiert, auch IControl implementieren. Das liegt daran, dass sowohl ITextBox als auch IListBoxdies erfordern.

interface IControl
{
    void Paint();
}

interface ITextBox requires IControl
{
    void SetText(String text);
}

interface IListBox requires IControl
{
    void SetItems(String[] items);
}

interface IComboBox requires ITextBox, IListBox
{
    ...
}

Eine Klasse kann null oder mehr Schnittstellen implementieren. Im nächsten Beispiel implementiert die Klasse EditBoxsowohl IControl als auch IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Für Windows-Runtime Typen in der Windows-Plattform wird eine Schnittstelle definiert, wenn Entwickler, die diese Typen nutzen, die die Schnittstelle implementieren möchten. Ein weiterer Anwendungsfall für die Definition einer Schnittstelle ist, wenn mehrere Laufzeitklassen die Schnittstelle implementieren, und Entwickler, die diese Laufzeitklassen verwenden, zugreifen auf unterschiedliche Objekttypen generischer (und somit polymorpher) über diese gemeinsame Schnittstelle.

Hinweis

Denken Sie zweimal daran, das requires Schlüsselwort in MIDL 3.0 zu verwenden. Es kann zu fehlerhaften Designs führen, insbesondere wenn die Versionierung berücksichtigt wird.

Enumerationen

Ein Aufzählungstyp (oder aufgezählter Typ oder Aufzählung) ist ein eindeutiger Werttyp mit einem Satz benannter Konstanten. Im folgenden Beispiel wird ein Aufzählungstyp namens Color mit drei Konstantenwerten definiert und verwendet: Rot, Grün und Blau.

enum Color
{
    Red,
    Green,
    Blue, // Trailing comma is optional, but recommended to make future changes easier.
};

Jeder Aufzählungstyp weist einen entsprechenden integralen Typ namens zugrunde liegender Typ des Aufzählungstyps auf. Der zugrunde liegende Typ einer Aufzählung ist entweder Int32 oder UInt32.

Die Windows-Runtime unterstützt zwei Arten von Aufzählungen: normale Aufzählungen und Kennzeichnungen. Eine Aufzählung der normalen Art drückt eine Reihe von exklusiven Werten aus; während einer der Flags-Art eine Reihe von Booleschen Werten darstellt. Um bitweise Operatoren für eine Flags-Aufzählung zu aktivieren, generiert der MIDL 3.0-Compiler C++-Operatorüberladungen.

Eine Flags-Aufzählung hat das [flags] Attribut angewendet. In diesem Fall ist der zugrunde liegende Typ der Aufzählung UInt32. Wenn das Attribut nicht vorhanden ist (eine normale Aufzählung), ist der [flags] zugrunde liegende Typ der Aufzählung Int32. Es ist nicht möglich, eine Aufzählung als andere Art zu deklarieren.

[flags]
enum SetOfBooleanValues
{
    None   = 0x00000000,
    Value1 = 0x00000001,
    Value2 = 0x00000002,
    Value3 = 0x00000004,
};

Das Speicherformat und der Bereich der möglichen Werte eines Typs werden durch den zugrunde liegenden Typ bestimmt. Der Satz von Werten, die ein Aufzählungstyp übernehmen kann, ist nicht durch seine deklarierten Aufzählungselemente beschränkt.

Im folgenden Beispiel wird ein Aufzählungstyp namens "Ausrichtung" mit einem zugrunde liegenden Typ von Int32 definiert.

enum Alignment
{
    Left = -1,
    Center = 0,
    Right = 1
};

Wie auch für C und C++gilt, kann eine MIDL 3.0-Aufzählung einen Konstantenausdruck enthalten, der den Wert des Elements angibt (wie oben dargestellt). Der Konstantenwert für jedes Enumenelement muss im Bereich des zugrunde liegenden Typs des Enums liegen. Wenn eine Aufzählungsmitgliedsdeklaration keinen Wert explizit angibt, wird das Element dem Wert Null (wenn es sich um das erste Element im Aufzählungstyp handelt) oder den Wert des textual vorangehenden Enumenumselements plus eins angegeben.

Im folgenden Beispiel wird ein Aufzählungstyp namens "Berechtigungen" mit einem zugrunde liegenden Typ von UInt32 definiert.

[flags]
enum Permissions
{
    None = 0x0000,
    Camera = 0x0001,
    Microphone = 0x0002
};

Attribute

Typen, Member und andere Entitäten in MIDL 3.0 unterstützen Modifizierer, die bestimmte Aspekte ihres Verhaltens steuern. Beispielsweise wird die Barrierefreiheit einer Methode mithilfe des protected Zugriffsmodifizierers gesteuert. MIDL 3.0 generalisiert diese Funktion, sodass benutzerdefinierte Arten von deklarativen Informationen an Programmentitäten angefügt werden können und zur Laufzeit aus den Metadaten abgerufen werden können.

Programme geben diese zusätzlichen deklarativen Informationen durch das Definieren und Verwenden von Attributen an.

Im nächsten Beispiel wird ein HelpAttribute-Attribut definiert, das auf Programmentitäten platziert werden kann, um Links zu ihrer zugeordneten Dokumentation bereitzustellen. Wie Sie sehen können, ist ein Attribut im Wesentlichen ein Strukturtyp, sodass es keinen Konstruktor hat und nur Datenelemente enthält.

[attributeusage(target_runtimeclass, target_event, target_method, target_property)]
attribute HelpAttribute
{
    String ClassUri;
    String MemberTopic;
}

Ein Attribut kann angewendet werden, indem er seinen Namen zusammen mit allen Argumenten innerhalb quadratischer Klammern vor der zugehörigen Deklaration angibt. Wenn der Name eines Attributs in Attribut endet, kann dieser Teil des Namens ausgelassen werden, wenn auf das Attribut verwiesen wird. Das HelpAttribute-Attribut kann beispielsweise wie folgt verwendet werden.

[Help("https://docs.contoso.com/.../BookSku", "BookSku class")]
runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
    [Help("https://docs.contoso.com/.../BookSku_Title", "Title method")]
    String Title;
}

Sie können das gleiche Attribut auf mehrere Deklarationen anwenden, indem Sie einen Bereichsblock verwenden, der dem Attribut folgt. Das heißt, ein Attribut gefolgt von Klammern, die die Deklarationen umgeben, auf die das Attribut angewendet wird.

runtimeclass Widget
{
    [Help("https://docs.contoso.com/.../Widget", "Widget members")]
    {
        void Display(String text);
        void Print();
        Single Rate;
    }
}

Attribute, die als Teil von Windows selbst implementiert wurden, befinden sich in der Regel im Windows.Foundation-Namespace .

Wie im ersten Beispiel gezeigt, verwenden Sie das [attributeusage(<target>)] Attribut in Ihrer Attributdefinition. Gültige Zielwerte sind target_all, target_delegatetarget_fieldtarget_interfacetarget_eventtarget_enumtarget_parametertarget_propertytarget_methodtarget_runtimeclassund .target_struct Sie können mehrere Ziele innerhalb der Klammern einschließen, getrennt durch Kommas.

Andere Attribute, die Sie auf ein Attribut anwenden können, sind [allowmultiple] und [attributename("<name>")].

Parametrisierte Typen

Das folgende Beispiel erzeugt Fehler MIDL2025: [msg]Syntaxfehler [kontext]: erwartet > oder in der Nähe ">>".

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String>> RetrieveCollectionAsync();

Fügen Sie stattdessen einen Leerzeichen zwischen den beiden > Zeichen ein, sodass das Zeichenpaar der Vorlage nicht als Rechtsverschiebungsoperator falsch interpretiert wird.

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String> > RetrieveCollectionAsync();

Im folgenden Beispiel wird fehler MIDL2025 erzeugt: [msg]Syntaxfehler [kontext]: erwartet > oder in der Nähe von "[". Dies liegt daran, dass es ungültig ist, ein Array als Parametertypargument für eine parameterisierte Schnittstelle zu verwenden.

Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();

Informationen zur Lösung finden Sie unter Zurückgeben eines Arrays asynchron.