Einführung in microsoft Interface Definition Language 3.0
Microsoft Interface Definition Language (MIDL) 3.0 ist eine vereinfachte, moderne Syntax zum Definieren von Windows-Runtime-Typen in IDL-Dateien (.idl
Dateien). Diese neue Syntax wird jedem vertraut sein, der mit C, C++, C# und/oder Java vertraut ist. MIDL 3.0 ist eine besonders bequeme Möglichkeit, C++/WinRT- Laufzeitklassen zu definieren, wesentlich präziser als frühere Versionen von IDL (reduzierung der Designs um zwei Drittel 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 werden.
// 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 Definieren Typen entwickelt wurde. Sie verwenden eine andere Programmiersprache, um diese Typen zu implementieren. Für die Verwendung von MIDL 3.0 benötigen Sie Windows SDK Version 10.0.17134.0 (Windows 10, Version 1803) (midl.exe
Version 8.01.0622 oder höher, verwendet mit der option /winrt
).
Anmerkung
Siehe auch die konsolidierte Windows-Runtime-Referenz (Das Windows-Runtime-Typsystemund Windows-Metadatendateien).
MIDL 1.0, 2.0 und 3.0
Interface Definition Language (IDL) begann mit dem Distributed Computing Environment/Remote Procedure Calls (DCE/RPC)-System. Die ursprüngliche MIDL 1.0- ist DCE/RPC IDL mit Verbesserungen für die Definition von COM-Schnittstellen und Coclasses.
Anschließend wurde eine aktualisierte MIDL 2.0-Syntax (auch als MIDLRT bezeichnet) 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 MIDL 2.0-Syntax geschrieben wurden. Dies 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 vorgesehen. Sie werden diese APIs nicht in diesem Formular erstellen oder nutzen (es sei denn, Sie schreiben Code auf sehr 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 C++/WinRT Laufzeitklassen. Die Header für die Verwendung aus C++/WinRT für die integrierten Windows-Runtime-APIs sind Teil des SDK, innerhalb des Ordners %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 durch die Auswahl, dass Windows-Runtime-Typen ausschließlich an windows-Runtime-APIs und von Windows-Runtime-APIs übergeben werden sollen. Obwohl es sich um eine gültige Entwurfsentscheidung handelt, 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 Interoperabilitätsszenarien gesehen werden, z. B. bei der Interoperabilität zwischen Direct3D und XAML. Da Sich Direct3D im Bild befindet, wird das Szenario unbedingt auf C++-Anwendungen beschränkt. Eine API, die eine COM-Schnittstelle erfordert, erzwingt also keine zusätzliche Einschränkung gegenüber und darüber, was inhärent ist. Beispielsweise kann eine C++-Anwendung eine IDXGISwapChain Schnittstellenzeiger abrufen und diese dann an die ISwapChainPanelNative::SetSwapChainChain-Methodeübergeben. Eine C#-Anwendung wäre beispielsweise nicht in der Lage, eine IDXGISwapChain- abzurufen, damit diese Methode aus diesem Grund nicht verwendet werden kann. Diese interopbezogenen Ausnahmen befinden sich in Interoperabilitätsheadern, wie z. B. windows.ui.xaml.media.dxinterop.h
.
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, die die COM-Komponente (z. B. DirectX) direkt erstellt und verwendet, und eine Replikation einiger Teilmenge der Features und Funktionen in Form einer Windows-Runtime-API-Oberfläche verfügbar macht, die Windows-Runtime-API-Oberfläche verwendet und zurückgibt. Nur Laufzeittypen. Sie könnten diese WRC dann aus einer Anwendung nutzen, die in jeder Windows-Runtime-Sprachprojektion geschrieben wurde.
Definitionsstruktur und Aufrufen von midl.exe über die 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 Elemente.
- Klassen, Schnittstellen, Strukturen und Enumerationen sind Typen.
- Methoden, Eigenschaften, Ereignisse und Felder sind Beispiele für Member.
Wenn Sie eine MIDL 3.0-Quelldatei kompilieren, gibt der Compiler (midl.exe
) eine Windows-Runtime-Metadatendatei (in 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 mit dem Namen Bookstore.BookSku. Es gibt keine sprachunabhängige Möglichkeit, BookSku- auszudrücken, ohne auch den Namespace auszudrücken.
Diese Klasse implementiert die Windows.UI.Xaml.Data.INotifyPropertyChanged Schnittstelle. Und die Klasse enthält mehrere Member: zwei Konstruktoren, eine Read-Write-Eigenschaft (Price), einige schreibgeschützte Eigenschaften (AuthorName über Title) und zwei Methoden namens Entspricht und ApplyDiscount. Beachten Sie die Verwendung des Typs single anstelle float-. Und dass Zeichenfolge einen Großbuchstaben "S" aufweist.
Trinkgeld
Visual Studio bietet die beste Oberfläche für die Kompilierung von MIDL 3.0 mithilfe der C++/WinRT Visual Studio-Erweiterung (VSIX). Siehe Visual Studio-Unterstützung für C++/WinRT und die 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 Bookstore.idl
gespeichert ist, können Sie den folgenden Befehl ausgeben. Bei Bedarf können Sie die im Befehl verwendete SDK-Versionsnummer (10.0.17134.0) aktualisieren.
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 erzeugt eine Metadatendatei mit dem Namen Bookstore.winmd
(standardmäßig wird der Name der .idl
Datei verwendet).
Trinkgeld
Wenn Sie mehrere IDL-Dateien verwenden (um dies zu beachten, lesen Sie Factoring-Laufzeitklassen in Midl-Dateien (IDL-Dateien)), führen Sie dann alle resultierenden .winmd
Dateien in einer einzelnen Datei mit demselben Namen wie der Stammnamespace zusammen. Diese endgültige .winmd
Datei ist der, auf den die Verbraucher Ihrer APIs verweisen.
In diesem Fall ist BookSku- die einzige Laufzeitklasse im Bookstore Namespace, daher haben wir einen Schritt gespeichert und die datei .idl
für den Namespace benannt.
Übrigens können Sie den Befehl where
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. Sie können z. B. die Win2D- Windows-Runtime-API für das Rendern von 2D-Grafiken im unmittelbaren Modus nutzen. Der obige Befehl verwendet den /reference
Switch, um auf eine Windows-Runtime-Metadatendatei (.winmd
) zu verweisen. In diesem nächsten Beispiel wird dieser Schalter erneut verwendet, wobei wir uns das Szenario vorstellt, in dem wir Bookstore.winmd
haben, 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 MVVMApp.idl
gespeichert ist, können Sie den folgenden Befehl ausstellen, um auf Bookstore.winmd
zu verweisen.
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. Er präfixt den Namen aller Typen, die im Bereich des Namespaceblocks definiert sind, mit dem Namespacenamen. 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 desselben Windows.Foundation.Uri Klasse (wie Sie sehen können, trennen Punkte die Ebenen geschachtelter Namespaces).
namespace Windows.Foundation
{
runtimeclass Uri : IStringable
{
...
}
}
namespace Windows
{
namespace Foundation
{
runtimeclass Uri : IStringable
{
...
}
}
}
Hier ist ein weiteres Beispiel, das zeigt, dass es zulässig 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 eine neue wie folgt zu öffnen.
namespace RootNs.SubNs1
{
runtimeclass MySubNs1Class
{
void DoWork();
}
}
namespace RootNs.SubNs1.SubNs2
{
runtimeclass MySubNs2Class
{
void DoWork();
}
}
Arten
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 Verweistyps speichert einen Verweis auf seine Daten (eine solche Variable wird auch als Objektbezeichnet).
Es ist möglich, dass zwei Verweistypvariablen auf dasselbe Objekt verweisen. Ein Vorgang für eine Variable wirkt sich daher auf das Objekt aus, auf das von der anderen Variablen 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-Wertetypen werden weiter in einfache Typen, Enumerationstypen, Strukturtypen und Nullwerte unterteilt.
DIE REFERENZtypen von MIDL 3.0 werden weiter in Klassentypen, Schnittstellentypen und Delegattypen unterteilt.
Hier ist eine Übersicht über das MidL 3.0-Typsystem. Im Gegensatz zu früheren Versionen von MIDL können Sie keine Aliase für diese Typen verwenden.
Kategorie | Beschreibung | |
---|---|---|
Werttypen | Einfache Typen | Signierter Integral: Int16, Int32, Int64 |
Nicht signiertes Integral: UInt8, UInt16, UInt32, UInt64 | ||
Unicode-Zeichen: Char- (stellt eine UTF-16LE dar; eine 16-Bit-Unicode-Codeeinheit) | ||
Unicode-Zeichenfolgen: Zeichenfolgen- | ||
IEEE-Gleitkomma: Single, Double | ||
Boolescher Wert: boolesche | ||
128-Bit-UUID: Guid- | ||
Enumerationstypen | Benutzerdefinierte Typen des Formulars Enumeration E {...} | |
Strukturtypen | Benutzerdefinierte Typen des Formulars Struktur S {...} | |
Nullable-Typen | Erweiterungen aller anderen Werttypen mit einem wert null | |
Referenztypen | Klassentypen | Ultimative Basisklasse aller anderen Typen: Object |
Benutzerdefinierte Typen des Formulars Laufzeitklasse C {...} | ||
Schnittstellentypen | Benutzerdefinierte Typen des Formulars Schnittstelle I {...} | |
Delegattypen | Benutzerdefinierte Typen des Formulars Delegaten <returnType> D(...) |
Die sieben integralen Typen bieten Unterstützung für nicht signierte 8-Bit-Daten; und 16-Bit-, 32-Bit- und 64-Bit-Werte im signierten oder nicht signierten Formular.
Die beiden Gleitkommatypen, Single und Double, stellen Daten dar, die jeweils die 32-Bit-Single-Precision- und 64-Bit-IEEE 754-Formate verwenden.
MIDL 3.0-boolesche Typ stellt boolesche Werte dar; entweder true
oder false
.
Zeichen und Zeichenfolgen in MIDL 3.0 enthalten Unicode-Zeichen. Der typ Char stellt eine UTF-16LE-Codeeinheit dar; und der String Typs stellt eine Sequenz von UTF-16LE-Codeeinheiten dar.
In der folgenden Tabelle sind die numerischen Typen von MIDL 3.0 zusammengefasst.
Kategorie | Bits | Art | 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 | |
Nicht signiertes Integral | 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 zum Erstellen neuer Typen. Eine Typdefinition gibt den Namen und die Member des neuen Typs an. Diese MIDL 3.0-Typkategorien sind vom Benutzer definierbar.
- Attributtypen,
- Strukturtypen,
- Schnittstellentypen,
- Laufzeitklassentypen,
- Delegattypen und
- Enumerationstypen.
Ein Attribut Typ 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.
Eine Struktur Typs definiert eine Windows-Runtime-Struktur, die Datenmember (Felder) enthält. Strukturen sind Werttypen und erfordern keine Heapzuordnung. Ein Datenmememm eines Strukturtyps muss entweder ein Werttyp oder ein nullwertbarer Typ sein. Strukturtypen unterstützen keine Vererbung.
Eine Schnittstelle Typ definiert eine Windows-Runtime-Schnittstelle, bei der es sich um einen benannten Satz von Funktionsmembern handelt. Eine Schnittstelle kann angeben, dass eine Implementierung der Schnittstelle auch eine oder mehrere angegebene zusätzliche (erforderliche) Schnittstellen implementieren muss. Jeder Schnittstellentyp wird direkt von der Windows-Runtime-IInspectable-Schnittstelle abgeleitet.
Eine Laufzeitklasse Typs definiert eine Windows-Runtime-Klasse (Laufzeitklasse). Eine Laufzeitklasse enthält Member, die Eigenschaften, Methoden und Ereignisse sein können.
Ein Delegat Typ definiert einen Windows-Runtime-Delegaten, der einen Verweis auf eine Methode mit einer bestimmten Parameterliste und einem bestimmten Rückgabetyp darstellt. Stellvertretungen ermöglichen die Behandlung einer Methode als Entität, die als Parameter übergeben werden kann. Ein Delegat ähnelt dem Konzept eines Funktionszeigers in einigen anderen Sprachen. Im Gegensatz zu Funktionszeigern sind Stellvertretungen objektorientiert und typsicher.
Ein Enumerationstyp Typs ist ein eindeutiger Typ mit benannten Konstanten. Jeder Enumerationstyp weist einen impliziten zugrunde liegenden Typ auf; entweder Int32- oder UInt32-. Der Wertesatz eines Enumerationstyps entspricht dem Wertesatz des zugrunde liegenden Typs.
MIDL 3.0 unterstützt drei zusätzliche Typkategorien.
- eindimensionale Arraytypen,
- Nullwertetypen und
- der Object- Typ.
Sie müssen kein eindimensionales Array deklarieren, bevor Sie es verwenden können. Arraytypen werden stattdessen anhand eines Typnamens mit eckigen Klammern erstellt. Beispielsweise ist Int32[] ein eindimensionales Array von Int32.
Ebenso müssen nullablen 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 null
enthalten kann. Beispielsweise ist Windows.Foundation.IReference<Int32-> ein Typ, der eine beliebige 32-Bit-Ganzzahl oder den Wert null
enthalten kann. Siehe auch IReference<T>.
Schließlich unterstützt MIDL 3.0 den Object Typ, der der Windows-Runtime-IInspectable--Schnittstelle zugeordnet ist. Die Schnittstelle und Laufzeitklassen Referenztypen, die konzeptionell vom typ Object abgeleitet sind; Stellvertretung nicht.
Ausdrücke in einem aufgezählten Wert
Mit MIDL 3.0 können Sie nur einen Ausdruck in der Definition des Werts der benannten Konstanten eines Aufzählungstyps verwenden; mit anderen Worten, in einem Enumerationsinitialisierer.
Ein Ausdruck wird aus Operanden und Operatorenerstellt. 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 eine höhere Rangfolge hat als der Operator +. Logische Vorgänge sind niedriger als bitweise Vorgänge.
In der folgenden Tabelle sind die Operatoren von MIDL 3.0 zusammengefasst, in der die Operatorkategorien in der Reihenfolge der Rangfolge von der höchsten bis zur niedrigsten Rangfolge aufgelistet werden. Operatoren in derselben Kategorie haben die gleiche Priorität.
Kategorie | Ausdruck | Beschreibung |
---|---|---|
Primär | x++ | Nach dem Inkrement |
x-- | Nach der Dekrementierung | |
Unär | +x | Identität |
-x | Negation | |
!x | Logische Negation | |
~x | Bitweise Negation | |
++x | Vorheriges Inkrement | |
--x | Vorschritt | |
Multiplikativ | x * y | Multiplikation |
x / y | Aufteilung | |
x % y | Rest | |
Additiv | x + y | Addition, Zeichenfolgenverkettung, Delegatkombination |
x – y | Subtraktion, Delegieren entfernen | |
Umschalten | x << y | Umschalt nach links |
x >> y | Umschalten nach rechts | |
Bitweise UND | x & y | Ganzzahlig UND |
Bitweiser XOR | x ^ y | Ganzzahliger XOR |
Bitweise ODER | x | y | Ganzzahlig ODER |
LogischeS UND | x && y | Boolescher logischer UND |
LogischeS ODER | x || y | Boolescher logischer OR |
Klassen
Klassen (oder Laufzeitklassen) sind die grundlegendsten Typen von MIDL 3.0. Eine Klasse ist eine Definition einer Aggregation von Methoden, Eigenschaften und Ereignissen in einer einzelnen Einheit. Klassen unterstützen Vererbung und Polymorphismus– Mechanismen, mit denen abgeleiteten KlassenBasisklassen erweitern und spezialisiert.
Sie definieren einen neuen Klassentyp mithilfe einer Klassendefinition. Eine Klassendefinition beginnt mit einem Header, der das schlüsselwort runtimeclass
, den Namen der Klasse, die Basisklasse (sofern angegeben) und die von der Klasse implementierten Schnittstellen angibt. Auf die Kopfzeile folgt der Klassentext, der aus einer Liste von Memberdeklarationen besteht, die zwischen den Trennzeichen { und }geschrieben wurden.
Hier ist eine Definition einer einfachen Klasse mit dem Namen Area.
runtimeclass Area
{
Area(Int32 width, Int32 height);
Int32 Height;
Int32 Width;
static Int32 NumberOfAreas { get; };
}
Dadurch wird eine neue Windows-Runtime-Klasse namens Areadefiniert, die über einen Konstruktor verfügt, der zwei Int32 Parameter, zwei Int32 Eigenschaften mit Lese-/Schreibzugriff namens Height und Widthund eine statische schreibgeschützte Eigenschaft namens NumberOfAreas.
Standardmäßig ist eine Laufzeitklasse versiegelt und abgeleitet von dieser nicht zulässig. Siehe Basisklassen.
Um XAML an ein Ansichtsmodell zu binden, muss die Laufzeitklasse des Ansichtsmodells in MIDL definiert werden. Siehe XAML-Steuerelemente; eine Bindung an eine C++/WinRT-Eigenschaft, um weitere Details zu erhalten.
Sie können deklarieren, dass eine Klasse keine Instanzen unterstützt (und daher nur statische Member enthalten darf), indem Sie der Laufzeitklassendefinition das Präfix static
Schlüsselwort voranstellen. Wenn Sie der Klasse ein nicht statisches Element hinzufügen, tritt dann ein Kompilierungsfehler auf.
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 der Laufzeitklassendefinition das Präfix partial
Schlüsselwort voranstellen. Alle partiellen Klassendefinitionen, die vom Compiler gefunden werden, werden in einer einzigen Laufzeitklasse kombiniert. Dieses Feature richtet sich in erster Linie an XAML-Erstellungsszenarien, in denen einige der partiellen Klassen computergeneriert werden.
Modifikator | Bedeutung |
---|---|
statisch | Die Klasse hat keine Instanzen. Folglich sind nur statische Member zulässig. |
teilweise | Die Klassendefinition ist unvollständig. |
Weitere Informationen finden Sie unter Kompositions- und Aktivierungs- für erweiterte Modifizierer.
Modifizierer für Den Memberzugriff
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 Mitglieds zu deklarieren. Alle Member sind implizit öffentlich. Aus diesem Grund erfordert MIDL 3.0 weder das (effektiv redundante) public
Schlüsselwort noch lässt es zu.
Basisklassen
Eine Klassendefinition kann eine Basisklasse angeben, indem Sie den Klassennamen und Typparametern mit einem Doppelpunkt und dem Namen der Basisklasse folgen. Das Weglassen einer Basisklassenspezifikation entspricht dem Ableiten vom Typ Object (d. h. von IInspectable).
Anmerkung
Ihre Ansichtsmodellklassen – tatsächlich alle Laufzeitklassen, 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 als komposierbare Klasse bezeichnet. Und es gibt Einschränkungen bei kompositablen Klassen. Damit eine Anwendung das zertifizierungskit für Windows-Apps Tests bestehen kann, die von Visual Studio und vom Microsoft Store verwendet werden, um Übermittlungen zu überprüfen (und damit die Anwendung erfolgreich in den Microsoft Store aufgenommen wird), muss eine komponierbare Klasse letztendlich von einer Windows-Basisklasse abgeleitet werden. Dies bedeutet, dass die Klasse am Stamm der Vererbungshierarchie ein Typ sein muss, der aus einem Windows.*-Namespace stammt.
Siehe XAML-Steuerelemente; eine Bindung an eine C++/WinRT-Eigenschaft, um weitere Details zu erhalten.
Im nächsten Beispiel ist die Basisklasse Volume-Area, und die Basisklasse von 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;
}
Anmerkung
Hier werden Area und Volume in derselben Quelldatei definiert. Eine Erläuterung der Vor- und Nachteile finden Sie unter Factoring-Laufzeitklassen in Midl-Dateien (IDL).
Eine Klasse erbt die Member der Basisklasse. Vererbung bedeutet, dass eine Klasse implizit alle Member ihrer Basisklasse enthält, mit Ausnahme der Konstruktoren der Basisklasse. Eine abgeleitete Klasse kann neue Member zu diesen hinzufügen, die sie erbt, aber sie kann die Definition eines geerbten Elements nicht entfernen.
Im vorherigen Beispiel erbt Volume die eigenschaften Height und Width von Area. Daher enthält jede Volume Instanz drei Eigenschaften: Height, Widthund Depth.
Im Allgemeinen erfordern Typauflösungsregeln, dass ein Typname vollständig qualifiziert ist, wenn auf sie verwiesen wird. Eine Ausnahme ist, wenn der Typ im selben Namespace wie der aktuelle Typ definiert wurde. Das obige Beispiel funktioniert wie geschrieben, wenn sich Area und Volume im selben Namespace befinden.
Implementierte Schnittstellen
Eine Klassendefinition kann auch eine Liste der Schnittstellen angeben, die von der Klasse implementiert werden. Sie geben die Schnittstellen als durch Trennzeichen getrennte Liste von 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 Member der Schnittstelle nicht für die Klasse. Natürlich müssen Sie sie für die tatsächliche Implementierung deklarieren und definieren.
Angehörige
Die Member einer Klasse sind entweder statische Member oder Instanzmember. Ein statisches Element gehört zu einer Klasse. Ein Instanzmemm gehört zu einem Objekt (d. a. eine Instanz einer Klasse).
Diese Tabelle enthält die Typen von Membern, die eine Klasse enthalten kann.
Elementtyp | Beschreibung |
---|---|
Erbauer | Zum Initialisieren einer Instanz der Klasse erforderliche Aktionen oder zum Initialisieren der Klasse selbst |
Eigenschaften | Aktionen, die mit dem Lesen und Schreiben benannter Eigenschaften einer Instanz der Klasse oder der Klasse selbst verknüpft sind |
Methodik | 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 |
Erbauer
MIDL 3.0 unterstützt die Deklaration von Instanzkonstruktoren. Ein Instanzkonstruktor ist eine Methode, die die zum Initialisieren einer Instanz einer Klasse erforderlichen Aktionen implementiert. 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. Beispielsweise deklariert die Test-Klasse unten drei Instanzkonstruktoren; eines ohne Parameter (der Standard--Konstruktor), einer, der einen Int32--Parameter akzeptiert, und einer, der zwei Double-Parameter (parametrisierten-Konstruktoren) akzeptiert.
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. Instanzkonstruktoren werden nicht geerbt (mit einer Ausnahme), und eine Klasse verfügt nicht über andere Instanzenkonstruktoren als die, die tatsächlich in der Klasse deklariert wurden. Wenn kein Instanzkonstruktor für eine Klasse bereitgestellt wird, können Sie die Klasse nicht direkt instanziieren. Für eine solche Klasse hätten Sie in der Regel eine Factorymethode an anderer Stelle, die eine Instanz der Klasse zurückgibt.
Die Ausnahme ist nicht gesiegelte Klassen. Eine nicht versiegelte Klasse kann einen oder mehrere geschützte Konstruktoren aufweisen.
Eigenschaften
Eigenschaften sind konzeptuell mit Feldern vergleichbar (z. B. C#-Felder oder die Felder einer MIDL 3.0-Struktur). Sowohl Eigenschaften als auch Felder sind Elemente mit einem Namen und einem zugeordneten Typ. Im Gegensatz zu Feldern bezeichnen Eigenschaften jedoch keine Speicherorte. Stattdessen verfügen Eigenschaften über Accessoren, die die auszuführende Funktion angeben, wenn Sie eine Eigenschaft lesen oder schreiben.
Eine Eigenschaft wird wie das Feld einer Struktur deklariert, mit der Ausnahme, dass die Deklaration mit einem get
Schlüsselwort und/oder einem set
Schlüsselwort endet, das zwischen den Trennzeichen { und }geschrieben 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.
Beispielsweise enthält die Klasse Area, die zuvor gesehen wurde, zwei Lese-/Schreibeigenschaften namens Height und Width.
unsealed runtimeclass Area
{
Int32 Height { get; set; };
Int32 Width; // get and set are implied if both are omitted.
}
Die Deklaration von Width lässt die geschweiften Klammern und die schlüsselwörter get
und set
weg. Die Auslassung impliziert, dass die Eigenschaft schreibgeschützt ist und semantisch identisch ist mit der Bereitstellung der get
und set
Schlüsselwörtern in dieser Reihenfolge –get
, gefolgt von set
.
Darüber hinaus können Sie nur das schlüsselwort get
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 schlüsselwort set
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 mit Lese-/Schreibzugriff versehen möchten und keine binäre Kompatibilität mit vorherigen Definitionen von Area beibehalten müssen (z. B. ist die Area-Klasse ein Typ in einer Anwendung, die Sie jedes Mal neu kompilieren), können Sie einfach das set
Schlüsselwort der vorhandenen SurfaceColor- Deklaration wie folgt hinzufügen.
unsealed runtimeclass Area
{
...
Color SurfaceColor { get; set; };
}
Wenn Sie dagegen binäre Stabilität benötigen (z. B. ist die Area Klasse eine Komponente in einer Bibliothek, die Sie an Kunden senden), können Sie das schlüsselwort set
der vorhandenen Eigenschaftsdeklaration nicht hinzufügen. Dadurch wird die binäre Schnittstelle in Ihre Klasse geändert.
Fügen Sie in diesem Fall die Eigenschaft set
Schlüsselwort 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 Get- und Set-Schlüsselwörter in der Eigenschaftendeklaration bestimmen die Reihenfolge der Get- und Set accessor-Methoden 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 Wertund keinem Rückgabetyp – dem Eigenschaftensatzer.
Folglich erzeugen diese beiden Deklarationen unterschiedliche binäre Schnittstellen.
Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Statische Eigenschaften und Instanzeigenschaften
Ähnlich wie bei Methoden unterstützt MIDL 3.0 sowohl Instanzeigenschaften als auch statische Eigenschaften. Statische Eigenschaften werden mit dem präfixierten static
Modifizierer deklariert, und Instanzeigenschaften werden ohne sie deklariert.
Methodik
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 mit 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 wird 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 in der Klasse eindeutig sein, in der die Methode deklariert wird. Die Signatur einer Methode besteht aus dem Namen der Methode, den Typen ihrer Parameter und/oder der Anzahl der Parameter. Die Signatur einer Methode enthält nicht den Rückgabetyp.
Modifizierer für Methodensichtbarkeit
Eine Methode kann einen von zwei optionalen Sichtbarkeitsmodifizierern aufweisen, wenn die Methode in einer abgeleiteten Klasse vorhanden ist.
Der überschreibbaren Modifizierer gibt an, dass diese Methode 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 Membern in einer nachfolgenden abgeleiteten Klasse zugänglich ist.
Methodenüberladung
Die Methode Ü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 Arity).
runtimeclass Test
{
static void F();
static void F(Double x);
static void F(Double x, Double y);
}
Anmerkung
Alle Methoden mit demselben Namen sollten unterschiedliche Aritäthaben. Das liegt daran, dass schwach typierte Programmiersprachen die Überladung nach Typ nicht unterstützen.
Parameter
Parameter werden verwendet, um Werte oder Variablenverweise an eine Methode zu übergeben. Ein Parameter beschreibt einen Steckplatz mit einem Typ und einem Namen sowie optional ein Zusatzwort. Ein Argument ist ein tatsächlicher Wert, der vom Aufrufer der Methode an den Angerufenen übergeben wird.
Parameter einer Methode rufen ihren Wert aus dem spezifischen -Argument ab, das angegeben wird, wenn die Methode aufgerufen wird. Die Art und Weise, wie Argumente zwischen Aufrufer und Angerufenen übergeben werden, hängt vom Typ des Parameters ab. Standardmäßig sind alle Parameter Eingabeparameter, d. h. sie werden nur vom Aufrufer an den Angerufenen gemarstet. Die Modifiziererstichwörter ref
, ref const
und out
können hinzugefügt werden, um die Standardrichtung des Marshallings zwischen Aufrufer und Angerufenen zu ändern und Ausgabeparameterzu erstellen. Nicht alle Schlüsselwörter sind jedoch bei allen Parametertypen gültig; Gültige Kombinationen sind unten aufgeführt.
Wichtig
Die Common Language Runtime (CLR) enthält Konzepte und Modifiziererstichwörter, die den in diesem Abschnitt beschriebenen ähnlich sein können. In der Praxis sind diese jedoch nicht miteinander verknüpft, und die Auswirkung dieser Modifizierer ist spezifisch für das Design und die Funktionsweise der Windows-Runtime.
Werttypen werden implizit Eingabeparameter, und standardmäßig wird eine Kopie des Arguments vom Aufrufer an den Angerufenen übergeben. Wertparameter können mit dem schlüsselwort
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 Vollkopie übergeben werden, durch Zeiger an die unveränderliche Struktur übergeben werden. Dies wird mit dem Schlüsselwort ref const
(nichtconst ref
) erreicht, das den Strukturparameter als Eingabeparameter kennzeichnet, aber den Marshaler anweist, 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 ist kein Boxen beteiligt. Dies ist eine praktische Wahl, wenn ein Wert so groß wie ein Matrix4x4akzeptiert wird, z. B..
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 und das Übergeben eines Verweises als Argument verantwortlich ist; Da das Argument jedoch ein Verweis auf das Objekt ist, werden Änderungen an diesem Objekt durch den Angerufenen nach dem Aufruf vom Aufrufer beobachtet. Alternativ kann ein Verweistyp mit dem schlüsselwort out
als Ausgabeparameter festgelegt werden. In diesem Fall werden die Rollen umgekehrt; der Angerufene ist die Zuweisung des Objekts und gibt es wieder an den Aufrufer zurück. Auch hier können die ref
Schlüsselwörter nicht allgemein mit Verweistypen verwendet werden (siehe Ausnahme unten).
runtimeclass Test
{
static void CreateObjectWithConfig(Config config, out MyClass newObject);
}
In der folgenden Tabelle wird das Verhalten der Marshallingstichwörter für Wertparameter und Referenzparameter zusammengefasst:
Benehmen | Zugewiesen von | Schlüsselwort | Arten | Bemerkungen |
---|---|---|---|---|
Eingabeparameter | Anrufer | (keine) | Alle Typen | Standardverhalten |
ref const |
Nur Struktur | Leistungsoptimierung | ||
Ausgabeparameter | Angerufener | 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 aufgerufen 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 eindimensionalen Arrays.
Ein Arrayparameter ist ein Verweistyp, und wie alle Verweistypen standardmäßig ein Eingabeparameter sind. In diesem Fall weist der Aufrufer das Array dem Angerufenen zu, der seine Elemente lesen kann, aber nicht ändern kann (schreibgeschützt). Dies wird als Übergeben Musters bezeichnet. Alternativ kann das Füllarray Muster verwendet werden, indem dem Parameter das schlüsselwort ref
hinzugefügt wird; in diesem Setup wird das Array weiterhin vom Aufrufer zugewiesen, ist aber konzeptionell ein Ausgabeparameter in dem Sinne, dass der Angerufene die Werte der Arrayelemente ausfüllt. Schließlich ist das letzte Muster die das Array empfangen, wobei (wie alle Ausgabereferenzparameter) der Angerufene sowohl das Zuordnen als auch das Initialisieren des Arguments darstellt, bevor es 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 wird das Verhalten für Arrays und deren Elemente zusammengefasst:
Arraymuster | Schlüsselwort | Zugewiesen von | Zugriff durch angerufenen Elemente |
---|---|---|---|
"Pass array" | (keine) | Anrufer | Schreibgeschützt |
"Füllarray" | ref |
Anrufer | Schreibgeschützt |
"Empfangsarray" | out |
Angerufener | Lese-/Schreibzugriff |
Weitere Informationen zur Verwendung von Arrayparametern im C-Stil – auch als konforme Arrays bezeichnet – mit C++/WinRT finden Sie unter Arrayparameter.
Statische Methoden und Instanzmethoden
Eine methode, die mit einem static
Modifiziererpräfix deklariert ist, ist eine statische Methode. Eine statische Methode hat keinen Zugriff auf eine bestimmte Instanz und kann daher nur direkt auf andere statische Member der Klasse zugreifen.
Eine ohne static
Modifizierer deklarierte Methode ist eine Instanzmethode.
Eine Instanzmethode hat Zugriff auf eine bestimmte Instanz und kann sowohl auf statische als auch auf Instanzmember der Klasse zugreifen.
Die folgende Entity-Klasse hat sowohl statische als auch Instanzm.
runtimeclass Entity
{
Int32 SerialNo { get; };
static Int32 GetNextSerialNo();
static void SetNextSerialNo(Int32 value);
}
Jede Entität Instanz enthält eine eigene Seriennummer (und vermutlich einige andere Informationen, die hier nicht angezeigt werden). Intern initialisiert der Entity-Konstruktor (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, für die Sie die Eigenschaft aufrufen, Methode abrufen.
Die GetNextSerialNo und SetNextSerialNo- statische Methoden können auf die interne nächste verfügbare Seriennummer zugreifen, statisches Element der Entity-Klasse.
Überschreibbare und geschützte Methoden
Alle Methoden in einem Windows-Runtime-Typ sind effektiv virtuell. Wenn eine virtuelle Methode aufgerufen wird, bestimmt der Laufzeittyp der Instanz, für die dieser Aufruf stattfindet, die tatsächliche Methodenimplementierung, die aufgerufen werden soll.
Eine Methode kann in einer abgeleiteten Klasse außer Kraft gesetzt werden. Wenn eine Instanzmethodendeklaration einen overridable
Modifizierer enthält, kann die Methode von abgeleiteten Klassen überschrieben werden. Ob eine abgeleitete Klasse tatsächlich eine überschreibbare Basisklassenmethode überschreibt, wird durch die Implementierung bestimmt; sie ist in den Metadaten nicht 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 Instanzmethodendeklaration einen protected
Modifizierer enthält, ist die Methode nur für abgeleitete Klassen sichtbar.
Ereignisse
Ein Ereignis Deklaration ist ein Element, das angibt, dass eine Klasse eine Ereignisquelle ist. Eine solche Ereignisquelle stellt Benachrichtigungen an jeden Empfänger bereit, der einen Delegaten implementiert (eine Methode mit einer bestimmten Signatur).
Sie deklarieren ein Ereignis mithilfe des Schlüsselworts event
gefolgt vom Namen des Delegattyps (der die erforderliche Methodensignatur beschreibt), gefolgt vom Namen des Ereignisses. Hier ist ein Beispielereignis, das einen vorhandenen Delegattyp von der Plattform verwendet.
runtimeclass Area
{
...
event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
...
}
Eine Ereignisdeklaration fügt der Klasse implizit zwei Methoden hinzu: eine Methode hinzufügen, die ein Client aufruft, um der Quelle einen Ereignishandler hinzuzufügen, und eine entfernen 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;
Standardmäßig werden zwei Parameter immer 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 hat, handelt es sich bei den Ereignisargumenten um ein Object, dessen Wert null ist.
Delegierte
Ein Delegattyp gibt eine Methode mit einer bestimmten Parameterliste und einem bestimmten 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, mit der Ausnahme, dass sie außerhalb einer Laufzeitklasse vorhanden ist und dem schlüsselwort delegate
vorangestellt ist.
Ein Delegat ermöglicht es, Methoden als Entitäten zu behandeln, die Variablen zugewiesen und als Parameter übergeben werden können. Stellvertretungen ähneln dem Konzept von Funktionszeigern in einigen anderen Sprachen. Im Gegensatz zu Funktionszeigern sind Stellvertretungen objektorientiert und typsicher.
Wenn wir den WindowSizeChangedEventHandler Delegattyp von der Plattform nicht verwenden möchten, können wir unseren eigenen Delegattyp definieren.
delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);
Eine Instanz unseres SizeChangedHandler- Delegattyps kann auf jede Methode verweisen, die zwei Argumente akzeptiert (ein Object, und ein WindowSizeChangedEventArgs), und gibt "void" zurück. Nachdem wir Strukturerläutert haben, können Sie auch den parameter WindowSizeChangedEventArgs Parameter durch eine Ereignisargumente eigenen Typ ersetzen.
Eine interessante und nützliche Eigenschaft eines Delegaten besteht darin, dass er die Klasse der Methode, auf die er verweist, nicht kennt oder interessiert. wichtig ist, dass die referenzierte Methode die gleichen Parameter und den Rückgabetyp wie der Delegat aufweist.
Sie können optional eine Delegatdeklaration mit [uuid(...)]
attributieren.
Siehe auch Delegaten, die HRESULT-zurückgeben.
Strukturen
Eine Struktur ist eine Datenstruktur, die Datenmember (Felder) enthalten kann. Im Gegensatz zu einer Klasse ist eine Struktur jedoch ein Werttyp.
Strukturen eignen sich besonders für kleine Datenstrukturen mit Wertsemantik. 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 bei der Anzahl der von einer Anwendung ausgeführten Speicherzuweisungen ausmachen.
Verwenden wir ein Beispiel zum Kontrast von Klassen und Strukturen. 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. Mit Point als Klasse implementiert, werden 101 separate Objekte instanziiert: eines für das Arrayobjekt selbst; und eines für jedes der 100 Point-Elemente.
class Test
{
static Test()
{
Point[] points = new Point[100];
for (Int32 i = 0; i < 100; ++i) points[i] = new Point(i, i);
}
}
Eine leistungsfähigere Alternative besteht darin, Point eine Struktur anstelle einer Klasse zu erstellen.
struct Point
{
Int32 x;
Int32 y;
};
Jetzt wird nur ein Objekt instanziiert – das Arrayobjekt selbst. Die Point-Elemente werden in der Zeile innerhalb des Arrays gespeichert; eine Speicheranordnung, die Prozessorcaches nutzen können, um leistungsstarke Effekte zu erzielen.
Das Ändern einer Struktur ist eine binäre Unterbrechungsänderung. Daher werden die im Rahmen von Windows selbst implementierten Strukturen nach der Einführung nicht mehr geändert.
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 stellt eine Schnittstelle keine Implementierungen der von ihr definierten Member bereit. Er gibt lediglich die Member an, die von jeder Klasse bereitgestellt werden müssen, die die Schnittstelle implementiert.
Schnittstellen können eine Klasse erfordern, 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 IListBox-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 EditBox sowohl 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 Schnittstelle implementieren sollen. Ein weiterer Anwendungsfall zum Definieren einer Schnittstelle besteht darin, dass mehrere Laufzeitklassen die Schnittstelle implementieren. Entwickler, die diese Laufzeitklassen verwenden, greifen über diese gemeinsame Schnittstelle generisch (und somit polymorph) auf verschiedene Objekttypen zu.
Anmerkung
Denken Sie zweimal daran, das schlüsselwort requires
in MIDL 3.0 zu verwenden. Es kann zu unübersichtlichen Designs führen, insbesondere wenn die Versionsverwaltung berücksichtigt wird.
Enumerationen
Ein Enumerationstyp (oder enumerationstyp) ist ein eindeutiger Werttyp mit einem Satz benannter Konstanten. Im folgenden Beispiel wird ein Enumerationstyp namens Color mit drei Konstantenwerten definiert und verwendet: Red, Greenund Blue.
enum Color
{
Red,
Green,
Blue, // Trailing comma is optional, but recommended to make future changes easier.
};
Jeder Enumerationstyp weist einen entsprechenden integralen Typ auf, der als zugrunde liegenden Typ des Enumerationstyps bezeichnet wird. Der zugrunde liegende Typ einer Enumeration ist entweder Int32- oder UInt32-.
Die Windows-Runtime unterstützt zwei Arten von Enumerationen: normalen Enumerationen und Flags Enumerationen. Eine Enumeration der normalen Art drückt eine Reihe ausschließlicher Werte aus; während eine der Flags-Art einen Satz boolescher Werte darstellt. Um bitweise Operatoren für eine Flags-Enumeration zu aktivieren, generiert der MIDL 3.0-Compiler C++-Operatorüberladungen.
Eine Flags-Enumeration weist das attribut "[flags]
" auf. In diesem Fall ist der zugrunde liegende Typ der Enumeration UInt32. Wenn das attribut [flags]
nicht vorhanden ist (eine normale Enumeration), ist der zugrunde liegende Typ der Enumeration Int32. Es ist nicht möglich, eine Enumeration als einen anderen Typ zu deklarieren.
[flags]
enum SetOfBooleanValues
{
None = 0x00000000,
Value1 = 0x00000001,
Value2 = 0x00000002,
Value3 = 0x00000004,
};
Das Speicherformat und der Bereich möglicher Werte eines Enumerationstyps werden durch den zugrunde liegenden Typ bestimmt. Der Wertesatz, den ein Enumerationstyp übernehmen kann, ist nicht durch seine deklarierten Enumerationsmember beschränkt.
Im folgenden Beispiel wird ein Enumerationstyp namens Alignmentmit 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-Enumeration einen Konstantenausdruck enthalten, der den Wert des Elements angibt (siehe oben). Der Konstantenwert für jedes Enumerationselement muss sich im Bereich des zugrunde liegenden Typs der Enumeration befinden. Wenn eine Enumerationsmemembe nicht explizit einen Wert angibt, erhält das Element den Wert Null (wenn es sich um das erste Element im Enumerationstyp handelt) oder den Wert des textmäßig vorangehenden Enumerationsmememm plus 1.
Im folgenden Beispiel wird ein Enumerationstyp namens Permissionsmit 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 so, dass benutzerdefinierte Typen deklarativer Informationen an Programmentitäten angefügt und zur Laufzeit aus den Metadaten abgerufen werden können.
Programme geben diese zusätzlichen deklarativen Informationen an, indem Attributedefiniert und verwendet werden.
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 Datenmember 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 in eckigen Klammern direkt vor der zugehörigen Deklaration angibt. Wenn der Name eines Attributs im Attribut endet, kann dieser Teil des Namens weggelassen werden, wenn auf das Attribut verwiesen wird. Beispielsweise kann das HelpAttribute-attribut 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 dasselbe Attribut auf mehrere Deklarationen anwenden, indem Sie einen Bereichsblock nach dem Attribut verwenden. Das heißt, ein Attribut gefolgt von geschweiften Klammern um die Deklarationen, 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 werden, befinden sich in der Regel im Windows.Foundation Namespace.
Wie im ersten Beispiel gezeigt, verwenden Sie das attribut "[attributeusage(<target>)]
" für die Attributdefinition. Gültige Zielwerte sind target_all
, target_delegate
, target_enum
, target_event
, target_field
, target_interface
, target_method
, target_parameter
, target_property
, target_runtimeclass
und 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 nahe ">>".
Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String>> RetrieveCollectionAsync();
Fügen Sie stattdessen ein Leerzeichen zwischen den beiden >
Zeichen ein, sodass das Paar von vorlagenschließenden Zeichen nicht als Operator für die rechte Umschalttaste falsch interpretiert wird.
Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String> > RetrieveCollectionAsync();
Das folgende Beispiel erzeugt Fehler MIDL2025: [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 parametrisierte Schnittstelle zu verwenden.
Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();
Informationen zur Lösung finden Sie unter Zurückgeben eines Arrays asynchron.