Introduzione a Microsoft Interface Definition Language 3.0

Microsoft Interface Definition Language (MIDL) 3.0 è una sintassi semplificata e moderna per definire i tipi di Windows Runtime all'interno dei file.idl IDL (Interface Definition Language). Questa nuova sintassi avrà familiarità con chiunque abbia familiarità con C, C++, C#e/o Java. MIDL 3.0 è un modo particolarmente pratico per definire le classi di runtime C++/WinRT , essendo notevolmente più conciso rispetto alle versioni precedenti di IDL (riducendo le progettazioni di due terzi in lunghezza e usando valori predefiniti ragionevoli per ridurre la necessità di decorare con attributi).

Ecco l'aspetto di MIDL 3.0; questo esempio illustra la maggior parte degli elementi della sintassi del linguaggio che probabilmente verranno usati.

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

Si noti che la sintassi di MIDL 3.0 è specificamente e progettata esclusivamente per definire i tipi. Si userà un linguaggio di programmazione diverso per implementare tali tipi. Per usare MIDL 3.0, è necessario Windows SDK versione 10.0.17134.0 (Windows 10, versione 1803) (midl.exe versione 8.01.0622 o successiva, usata con l'opzione/winrt).

Nota

Vedere anche il riferimento consolidato Windows Runtime (I file di tipo Windows Runtime e Metadati di Windows).

MIDL 1.0, 2.0 e 3.0

Interface Definition Language (IDL) ha iniziato con il sistema Distributed Computing Environment/Remote Procedure Call (DCE/RPC). L'originale MIDL 1.0 è DCE/RPC IDL con miglioramenti per la definizione di interfacce COM e coclassi.

Una sintassi MIDL 2.0 aggiornata (nota anche come MIDLRT) è stata quindi sviluppata all'interno di Microsoft per dichiarare le API Windows Runtime per la piattaforma Windows. Se si guarda nella cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt Windows SDK, verranno visualizzati esempi di .idl file scritti con la sintassi MIDL 2.0. Queste API sono predefinite Windows Runtime, dichiarate nel modulo ABI (Application Binary Interface). Questi file esistono principalmente per gli strumenti da usare, non si creano né si usano queste API in questo modulo (a meno che non si stia scrivendo codice di livello molto basso).

Vedere anche Transizione a MIDL 3.0 dalla versione classica di MIDLRT.

MIDL 3.0 è una sintassi molto più semplice e più moderna, il cui scopo è dichiarare Windows Runtime API. È inoltre possibile usarlo nei progetti, in particolare per definire le classi di runtime C++/WinRT . Le intestazioni, per l'uso da C++/WinRT, per le API predefinite di Windows Runtime fanno parte dell'SDK, all'interno della cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Casi d'uso per MIDL 3.0

In generale, tutte le API Windows Runtime sono progettate per essere disponibili per tutte le proiezioni del linguaggio Windows Runtime. Questa operazione viene eseguita, in parte, scegliendo di passare esclusivamente i tipi di Windows Runtime a e dalle API Windows Runtime. Sebbene sia una decisione di progettazione valida per passare un'interfaccia COM non elaborata a e da un'API Windows Runtime, in questo modo limita i consumer di tale particolare API Windows Runtime alle applicazioni C++. La tecnica può essere vista negli scenari di interoperabilità, ad esempio durante l'interoperabilità tra Direct3D e XAML. Poiché Direct3D si trova nell'immagine, lo scenario è necessariamente limitato alle applicazioni C++. Quindi, un'API che richiede un'interfaccia COM non impone alcuna limitazione aggiuntiva oltre e sopra ciò che è intrinseco. Ad esempio, un'applicazione C++ può ottenere un puntatore dell'interfaccia IDXGISwapChain e quindi passarlo al metodo ISwapChainPanelNative::SetSwapChain. Un'applicazione C#, ad esempio, non sarebbe in grado di ottenere un IDXGISwapChain per iniziare, quindi non sarebbe in grado di usare tale metodo per tale motivo. Queste eccezioni correlate all'interoperabilità si trovano nelle intestazioni di interoperabilità, ad esempio windows.ui.xaml.media.dxinterop.h.

Se sono presenti funzionalità o funzionalità di un componente COM che si desidera esporre alle proiezioni del linguaggio Windows Runtime oltre C++, è possibile creare un componente di Windows Runtime C++ che crea e usa direttamente il componente COM (ad esempio DirectX, ad esempio) ed espone una replica di alcuni sottoinsieme delle relative funzionalità e funzionalità sotto forma di un elemento Windows Runtime superficie API che accetta e restituisce solo tipi di Windows Runtime. È quindi possibile usare WRC da un'applicazione scritta in qualsiasi proiezione del linguaggio di Windows Runtime.

Struttura di definizione e chiamata midl.exe dalla riga di comando

I concetti aziendali principali in una definizione MIDL 3.0 sono spazi dei nomi, tipi e membri. Un file di origine MIDL 3.0 (un .idl file) contiene almeno uno spazio dei nomi, all'interno dei quali sono tipi e/o spazi dei nomi subordinati. Ogni tipo contiene zero o più membri.

  • Le classi, le interfacce, le strutture e le enumerazioni sono tipi.
  • I metodi, le proprietà, gli eventi e i campi sono esempi di membri.

Quando si compila un file di origine MIDL 3.0, il compilatore () genera un file di metadati Windows Runtime (midl.exein genere un .winmd file).

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

Poiché lo spazio dei nomi di un tipo Windows Runtime diventa parte del nome del tipo, l'esempio precedente definisce una classe di runtime denominata Bookstore.BookSku. Non esiste un modo indipendente dal linguaggio di esprimere BookSku senza esprimere anche lo spazio dei nomi.

Questa classe implementa l'interfaccia Windows.UI.Xaml.Data.INotifyPropertyChanged . E la classe contiene diversi membri: due costruttori, una proprietà di lettura-scrittura (Prezzo), alcune proprietà di sola lettura (AuthorName through Title) e due metodi, denominati Equals e ApplyDiscount. Si noti l'uso del tipo Single anziché float. E che String ha un maiuscolo "S".

Suggerimento

Visual Studio offre l'esperienza migliore per la compilazione di MIDL 3.0, tramite L'estensione C++/WinRT Visual Studio (VSIX). Vedere Supporto di Visual Studio per C++/WinRT e VSIX.

È anche possibile compilare MIDL 3.0 dalla riga di comando. Se il codice sorgente per questo esempio viene archiviato in un file denominato Bookstore.idl, è possibile eseguire il comando seguente. Se necessario per il caso, è possibile aggiornare il numero di versione SDK usato nel comando (ovvero 10.0.17134.0).

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

Lo midl.exe strumento compila l'esempio e produce un file di metadati denominato Bookstore.winmd (per impostazione predefinita, viene usato il nome del .idl file).

Suggerimento

Se si utilizzano più file IDL (per consigli su questo, vedere Classi di runtime di Factoring in file Midl (.idl), quindi unire tutti i file risultanti .winmd in un singolo file con lo stesso nome dello spazio dei nomi radice. Tale file finale .winmd sarà quello a cui farà riferimento i consumer delle API.

In questo caso , BookSku è l'unica classe di runtime nello spazio dei nomi Bookstore , quindi è stato salvato un passaggio ed è stato appena denominato il file per lo .idl spazio dei nomi.

In caso contrario, è possibile usare il where comando per scoprire dove midl.exe è installato.

where midl

Se si desidera usare i tipi definiti in un file da un .idl file diverso .idl , usare la import direttiva. Per altri dettagli e un esempio di codice, vedere Controlli XAML; associare a una proprietà C++/WinRT. Naturalmente, se si usa un componente predefinito o di terze parti, non si avrà accesso al .idl file. Ad esempio, è possibile usare l'API Win2D Windows Runtime per il rendering 2D 2D in modalità immediata. Il comando precedente ha usato l'opzione /reference per fare riferimento a un file di metadati Windows Runtime (.winmd). In questo esempio successivo si userà di nuovo tale opzione, immaginando lo scenario in cui è disponibile Bookstore.winmd, ma non Bookstore.idl.

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

Se il codice sorgente dell'esempio precedente viene archiviato in un file denominato MVVMApp.idl, è possibile eseguire il comando seguente per fare riferimento Bookstore.winmda .

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

Spazi dei nomi

È necessario uno spazio dei nomi. Prefissi il nome di tutti i tipi definiti nell'ambito del blocco dello spazio dei nomi con il nome dello spazio dei nomi. Uno spazio dei nomi può anche contenere dichiarazioni dello spazio dei nomi subordinate. Il nome dei tipi definiti in un ambito dello spazio dei nomi subordinato ha un prefisso di tutti i nomi dello spazio dei nomi contenenti.

Gli esempi seguenti sono due modi per dichiarare la stessa classe Windows.Foundation.Uri (come si può vedere, i periodi separano i livelli di spazi dei nomi annidati).

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

Ecco un altro esempio che mostra che è legale dichiarare spazi dei nomi e i relativi tipi in modo annidato.

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

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

Ma è più comune chiudere lo spazio dei nomi precedente e aprire uno nuovo, come questo.

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

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

Tipi

Esistono due tipi di dati in MIDL 3.0: tipi di valore e tipi di riferimento. Una variabile di un tipo di valore contiene direttamente i dati. Una variabile di un tipo di riferimento archivia un riferimento ai relativi dati (tale variabile è nota anche come oggetto).

È possibile che due variabili di tipo di riferimento facciano riferimento allo stesso oggetto. Di conseguenza, un'operazione su una variabile influisce sull'oggetto a cui fa riferimento l'altra variabile. Con i tipi di valore, le variabili hanno la propria copia dei dati e non è possibile che un'operazione su uno influisca sull'altra.

I tipi di valore MIDL 3.0 sono ulteriormente suddivisi in tipi semplici, tipi di enumerazione, tipi di struct e tipi nullable.

I tipi di riferimento MIDL 3.0 sono ulteriormente suddivisi in tipi di classe, tipi di interfaccia e tipi di delegato.

Ecco una panoramica del sistema di tipi MIDL 3.0. A differenza delle versioni precedenti di MIDL, non è possibile usare alias per questi tipi.

Category Descrizione
Tipi valore Tipi semplici Integrale firmato: Int16, Int32, Int64
Integrale senza segno: UInt8,UInt16, UInt32, UInt64
Caratteri Unicode: Char (rappresenta un codice Unicode UTF-16LE; un'unità di codice Unicode a 16 bit)
Stringhe Unicode: String
Virgola mobile IEEE: Single, Double
Boolean: Boolean
UUID a 128 bit: Guid
Tipi enum Tipi definiti dall'utente dell'enumerazione del modulo E {...}
Tipi struct Tipi definiti dall'utente dello struct del modulo S {...}
Tipi nullable Estensioni di tutti gli altri tipi di valore con un valore Null
Tipi riferimento Tipi di classe Classe di base finale di tutti gli altri tipi: Object
Tipi definiti dall'utente della classe di runtime del modulo C {...}
Tipi interfaccia Tipi definiti dall'utente dell'interfaccia del modulo I {...}
Tipi delegato Tipi definiti dall'utente del delegato <del modulo returnType> D (...)

I sette tipi integrali forniscono supporto per i dati senza segno a 8 bit; e valori a 16 bit, a 32 bit e a 64 bit in formato firmato o senza segno.

I due tipi a virgola mobile, Single and Double, rappresentano i dati usando rispettivamente i formati IEEE 754 a precisione singola a 32 bit e a 64 bit.

Il tipo booleano MIDL 3.0 rappresenta i valori booleani; o truefalse.

I caratteri e le stringhe in MIDL 3.0 contengono caratteri Unicode. Il tipo Char rappresenta un'unità di codice UTF-16LE; e il tipo String rappresenta una sequenza di unità di codice UTF-16LE.

La tabella seguente riepiloga i tipi numerici di MIDL 3.0.

Category BITS Tipo Intervallo/Precisione
Integrale firmato 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
Unsigned Integer 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
Virgola mobile 32 Singolo 1,5 × 10-45 a 3,4 × 1038, precisione a 7 cifre
64 Double 5.0 × 10-324 a 1,7 × 10308, precisione a 15 cifre

I file di origine MIDL 3.0 usano definizioni di tipo per creare nuovi tipi. Una definizione di tipo specifica il nome e i membri del nuovo tipo. Queste categorie di tipi MIDL 3.0 sono definibili dall'utente.

  • tipi di attributo,
  • tipi di struct,
  • tipi di interfaccia,
  • tipi di runtimeclasse,
  • tipi di delegato e
  • tipi di enumerazione.

Un tipo di attributo definisce un attributo Windows Runtime che può essere applicato ad altre definizioni di tipo. Un attributo fornisce metadati sul tipo a cui viene applicato l'attributo.

Un tipo di struct definisce una struttura Windows Runtime che contiene membri dei dati (campi). Gli struct sono tipi di valore e non richiedono l'allocazione dell'heap. Un membro dati di un tipo struct deve essere un tipo valore o un tipo nullable. I tipi di struct non supportano l'ereditarietà.

Un tipo di interfaccia definisce un'interfaccia Windows Runtime, ovvero un set denominato di membri della funzione. Un'interfaccia può specificare che un'implementazione dell'interfaccia deve anche implementare una o più interfacce aggiuntive (necessarie). Ogni tipo di interfaccia deriva direttamente dall'interfaccia IInspectable Windows Runtime.

Un tipo di runtimeclasse definisce una classe Windows Runtime (classe di runtime). Una classe di runtime contiene membri che possono essere proprietà, metodi ed eventi.

Un tipo delegato definisce un delegato Windows Runtime, che rappresenta un riferimento a un metodo con un determinato elenco di parametri e un tipo restituito. I delegati consentono di considerare un metodo come entità che può essere passata come parametro. Un delegato è simile al concetto di puntatore a una funzione trovato in alcune altre lingue. A differenza dei puntatori alle funzioni, i delegati sono orientati agli oggetti e di tipo sicuro.

Un tipo enumerazione è un tipo distinto con costanti denominate. Ogni tipo di enumerazione ha un tipo sottostante implicito; Int32 o UInt32. Il set di valori di un tipo enumerazione è uguale al set di valori del tipo sottostante.

MIDL 3.0 supporta tre categorie di tipi aggiuntive.

  • Tipi di matrice a multidimensionale,
  • Tipi di valore nullable e
  • Tipo di oggetto .

Non è necessario dichiarare una matrice multidimensionale singola prima di poterla usare. Al contrario, i tipi matrice vengono costruiti facendo seguire a un nome di tipo delle parentesi quadre. Ad esempio, Int32[] è una matrice multidimensionale di Int32.

Analogamente, i tipi di valore nullable non devono essere definiti prima di poter essere usati. Per ogni tipo di valore non nullable T (ad eccezione di String), è presente un tipo nullable corrispondente Windows.Foundation.IReference<T>, che può contenere il valore nullaggiuntivo . Ad esempio, Windows.Foundation.IReference<Int32 è un tipo che può contenere qualsiasi intero a 32> bit o il valore null. Vedere anche IReference<T>.

Infine, MIDL 3.0 supporta il tipo oggetto, che esegue il mapping all'interfaccia IInspectable Windows Runtime. I tipi di riferimento dell'interfaccia e della classe di runtime derivano in modo concettuale dal tipo Oggetto ; delegato non.

Espressioni in un valore enumerato

Con MIDL 3.0 è possibile usare solo un'espressione nella definizione del valore delle costanti denominate di un tipo enumerato; in altre parole, in un inizializzatore di enumerazione.

Un'espressione viene costruita da operandi e operatori. Gli operatori in un'espressione indicano quali operazioni applicare agli operandi. Esempi di operatori includono +, -, *, /e new. mentre i valori effettivi, i campi, le variabili locali e le espressioni sono esempi di operandi.

Quando un'espressione contiene più operatori, la precedenza degli operatori controlla l'ordine in cui vengono valutati i singoli operatori. Ad esempio, l'espressione x + y * z viene valutata come x + (y * z) perché l'operatore * ha una precedenza superiore rispetto all'operatore +. Le operazioni logiche sono più basse rispetto alle operazioni bit per bit.

La tabella seguente riepiloga gli operatori MIDL 3.0, elencando le categorie di operatore in ordine di precedenza dal più alto al minimo. Gli operatori nella stessa categoria hanno la stessa precedenza.

Categoria Espressione Descrizione
Primaria x++ Post-incremento
x-- Post-decremento
Unario +x Identità
-X Negazione
!x Negazione logica
~x Negazione bit per bit
++x Pre-incremento
--x Pre-decremento
Moltiplicazione x * y Moltiplicazione
x / y Divisione
x % y Resto
Additive x + y Aggiunta, concatenazione stringhe, combinazione di delegati
x – y Sottrazione, rimozione di delegati
MAIUSC x << y Spostamento a sinistra
x >> y Spostamento a destra
AND bit per bit x & y Intero bit per bit AND
XOR bit per bit x ^ y XOR bit per bit intero
OR bit per bit x | y OR bit per bit intero
AND logico x && y AND logico booleano
OR logico x || y OR logico booleano

Classi

Le classi (o le classi di runtime) sono le classi più fondamentali dei tipi MIDL 3.0. Una classe è una definizione di aggregazione di metodi, proprietà ed eventi in una singola unità. Le classi supportano l'ereditarietà e il polimorfismo: meccanismi in cui le classi derivate possono estendere ed specializzazione le classi di base.

Si definisce un nuovo tipo di classe usando una definizione di classe. Una definizione di classe inizia con un'intestazione che specifica la runtimeclass parola chiave, il nome della classe, la classe base (se specificato) e le interfacce implementate dalla classe. L'intestazione è seguita dal corpo della classe, costituito da un elenco di dichiarazioni membro scritte tra i delimitatori { e }.

Ecco una definizione di una classe semplice denominata Area.

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

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

In questo modo viene definita una nuova classe Windows Runtime denominata Area, che ha un costruttore che accetta due parametri Int32, due proprietà di lettura-scrittura denominate Height e Width e una proprietà di sola lettura statica denominata NumberOfAreas.

Per impostazione predefinita, una classe di runtime è bloccata e la derivazione da essa non è consentita. Vedere Classi di base.

Per associare XAML a un modello di visualizzazione, la classe di runtime del modello di visualizzazione deve essere definita in MIDL. Per altri dettagli, vedi Controlli XAML, binding a una proprietà C++/WinRT.

È possibile dichiarare che una classe non supporta istanze (e di conseguenza deve contenere solo membri statici) prefisso la definizione della classe di runtime con la static parola chiave. L'aggiunta di un membro non statico alla classe genera quindi un errore di compilazione.

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

Una classe statica è diversa da una classe vuota. Vedere Anche classi vuote.

È possibile indicare che una definizione di classe è incompleta prefissindo la definizione della classe di runtime con la partial parola chiave. Tutte le definizioni di classe parziale rilevate dal compilatore vengono combinate in una singola classe di runtime. Questa funzionalità è principalmente per gli scenari di creazione XAML, in cui alcune delle classi parziali vengono generate dal computer.

Modificatore Significato
static La classe non ha istanze. Di conseguenza, sono consentiti solo membri statici.
partial La definizione della classe è incompleta.

Vedere Composizione e attivazione per modificatori avanzati.

Modificatori di accesso ai membri

Come MIDL 3.0 è un linguaggio di definizione per descrivere la superficie pubblica dei tipi di Windows Runtime, non è necessario che la sintassi esplicita dichiari l'accessibilità pubblica di un membro. Tutti i membri sono implicitamente pubblici. Ecco perché MIDL 3.0 non richiede né consente la parola chiave (con ridondanza effettiva). public

Classi di base

Una definizione di classe può specificare una classe di base seguendo il nome della classe e i parametri di tipo con due punti e il nome della classe base. L'omettezione di una specifica della classe di base è uguale a quella derivata dal tipo Object (in altre parole, da IInspectable).

Nota

Le classi del modello di visualizzazione, ovvero qualsiasi classe di runtime definita nell'applicazione, non deve derivare da una classe di base.

Qualsiasi classe di runtime definita nell'applicazione che deriva da una classe di base è nota come classe componibile . Le classi componibili sono soggette a limitazioni. Affinché un'applicazione superi i test del Kit di certificazione app Windows utilizzato da Visual Studio e da Microsoft Store per convalidare gli invii, e possa essere inserita correttamente nel sistema di Microsoft Store, una classe componibile in ultima analisi deve derivare da una classe di base di Windows. Vale a dire che la classe radice della gerarchia di ereditarietà deve essere un tipo che ha origine in uno spazio dei nomi Windows.*.

Per altri dettagli, vedi Controlli XAML, binding a una proprietà C++/WinRT.

Nell'esempio successivo, la classe base di Volume è Area e la classe di base di Area è 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;
}

Nota

In questo caso, Area e Volume vengono definiti nello stesso file di origine. Per una descrizione dei vantaggi e dei contro, vedere Classi di runtime di Factoring nei file Midl (con estensione idl).

Una classe eredita i membri della relativa classe di base. L'ereditarietà indica che una classe contiene in modo implicito tutti i membri della relativa classe di base, ad eccezione dei costruttori della classe base. Una classe derivata può aggiungere nuovi membri a quelli ereditati, ma non può rimuovere la definizione di un membro ereditato.

Nell'esempio precedente Volume eredita le proprietà Height e Width da Area. Ogni istanza di Volume contiene quindi tre proprietà: Height, Width e Depth.

In generale, le regole di risoluzione dei tipi richiedono che un nome di tipo sia completo quando viene fatto riferimento. Un'eccezione è quando il tipo è stato definito nello stesso spazio dei nomi del tipo corrente. L'esempio precedente funziona come scritto se Area e Volume si trovano entrambi nello stesso spazio dei nomi.

Interfacce implementate

Una definizione di classe può anche specificare un elenco di interfacce implementate dalla classe . Le interfacce vengono specificate come elenco delimitato da virgole di interfacce che seguono la classe di base (facoltativa).

Nell'esempio seguente, la classe Area implementa l'interfaccia IStringable ; e la classe Volume implementa sia IStringable che l'interfaccia ipotetica IEquatable .

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 MIDL non si dichiarano i membri dell'interfaccia nella classe . Naturalmente, è necessario dichiararli e definirli nell'implementazione effettiva.

Membri

I membri di una classe sono membri statici o membri dell'istanza. Un membro statico appartiene a una classe. Un membro dell'istanza appartiene a un oggetto , ovvero un'istanza di una classe .

Questa tabella mostra i tipi di membro che una classe può contenere.

Tipo di membro Descrizione
Costruttori Azioni necessarie per inizializzare un'istanza della classe o per inizializzare la classe stessa
Proprietà Azioni associate alla lettura e alla scrittura di proprietà denominate di un'istanza della classe o della classe stessa
Metodi Calcoli e azioni che possono essere eseguiti da un'istanza della classe o dalla classe stessa
Eventi Notifiche che possono essere generate da un'istanza della classe

Costruttori

MIDL 3.0 supporta la dichiarazione dei costruttori di istanza. Un costruttore di istanza è un metodo che implementa le azioni necessarie per inizializzare un'istanza di una classe. I costruttori potrebbero non essere statici.

Un costruttore viene dichiarato come un metodo di istanza (ma senza tipo restituito) e con lo stesso nome della classe contenitore.

È possibile eseguire l'overload dei costruttori di istanza. Ad esempio, la classe Test seguente dichiara tre costruttori di istanza; uno senza parametri (il costruttore predefinito ), uno che accetta un parametro Int32 e uno che accetta due parametri Double (costruttori con parametri ).

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

Per informazioni dettagliate sulla sintassi per gli elenchi di parametri, vedere Metodi di seguito.

Le proprietà, i metodi e gli eventi dell'istanza vengono ereditati. I costruttori di istanza non vengono ereditati (con un'eccezione) e una classe non ha costruttori di istanza diversi da quelli effettivamente dichiarati nella classe . Se non viene fornito alcun costruttore di istanza per una classe, non è possibile creare direttamente un'istanza della classe. Per una classe di questo tipo, in genere è presente un metodo factory altrove che restituisce un'istanza della classe .

L'eccezione è classi non sealed. Una classe non bloccato può avere uno o più costruttori protetti.

Proprietà

Le proprietà sono concettualmente simili ai campi ,ad esempio campi C# o campi di uno struct MIDL 3.0. Entrambe le proprietà e i campi sono membri con un nome e un tipo associato. Tuttavia, a differenza dei campi, le proprietà non indicano i percorsi di archiviazione. Le proprietà invece dispongono di funzioni di accesso che specificano la funzione da eseguire quando si legge o si scrive una proprietà.

Una proprietà viene dichiarata come il campo di uno struct, ad eccezione del fatto che la dichiarazione termina con una get parola chiave e/o una set parola chiave scritta tra i delimitatori { e }e terminando con un punto e virgola.

Una proprietà con una get parola chiave e una set parola chiave è una proprietà di lettura/scrittura. Una proprietà che ha solo una get parola chiave è una proprietà di sola lettura. Il Windows Runtime non supporta le proprietà di sola scrittura.

Ad esempio, la classe Area, vista in precedenza, contiene due proprietà di lettura/scrittura denominate Height e Width.

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

La dichiarazione di Width omette le parentesi graffe e le get parole chiave e set . L'omissione implica che la proprietà è di lettura/scrittura ed è semanticamente identica a fornire le get parole chiave e set in tale ordineget, seguite da set.

Inoltre, è possibile specificare solo la get parola chiave per indicare che la proprietà è di sola lettura.

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

Il Windows Runtime non supporta le proprietà di sola scrittura. È tuttavia possibile specificare solo la set parola chiave per modificare una proprietà di sola lettura esistente in una proprietà di lettura/scrittura. Prendere questa versione di Area come esempio.

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

Se successivamente vuoi rendere la proprietà SurfaceColor in lettura/scrittura e non devi mantenere la compatibilità binaria con le definizioni precedenti di Area (ad esempio, la classe Area è un tipo in un'applicazione che ricompila ogni volta), puoi semplicemente aggiungere la set parola chiave alla dichiarazione SurfaceColor esistente come questa.

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

Se, d'altra parte , è necessaria la stabilità binaria (ad esempio, la classe Area è un componente in una libreria spedita ai clienti), non è possibile aggiungere la set parola chiave alla dichiarazione di proprietà esistente. In questo modo l'interfaccia binaria viene modificata nella classe .

In tal caso, aggiungere la parola chiave property set a una definizione aggiuntiva della proprietà alla fine della classe come questa.

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

Il compilatore genera un errore per una proprietà di sola scrittura. Ma questo non è quello che viene fatto qui. A causa della dichiarazione precedente della proprietà come sola lettura, l'aggiunta della parola chiave set non dichiara una proprietà di sola scrittura, ma una proprietà di lettura/scrittura.

L'implementazione Windows Runtime di una proprietà è uno o due metodi della funzione di accesso in un'interfaccia. L'ordine delle parole chiave get e set nella dichiarazione di proprietà determina l'ordine dei metodi della funzione di accesso get e set nell'interfaccia di backup.

La get funzione di accesso corrisponde a un metodo senza parametri con un valore restituito del tipo di proprietà, ovvero il getter della proprietà.

Una set funzione di accesso corrisponde a un metodo con un singolo parametro denominato value e nessun tipo restituito, ovvero il setter della proprietà.

Di conseguenza, queste due dichiarazioni producono interfacce binarie diverse.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Proprietà statiche e dell'istanza

Analogamente ai metodi, MIDL 3.0 supporta sia le proprietà dell'istanza che le proprietà statiche. Le proprietà statiche vengono dichiarate con il prefisso del modificatore e le proprietà dell'istanza static vengono dichiarate senza di essa.

Metodi

Un metodo è un membro che implementa un calcolo o un'azione che può essere eseguita da un'istanza della classe o dalla classe stessa. È possibile accedere a un metodo statico tramite la classe . È possibile accedere a un metodo di istanza tramite un'istanza della classe .

Un metodo ha un elenco (possibilmente vuoto) di parametri, che rappresentano valori o riferimenti a variabili passati al metodo . Un metodo ha anche un tipo restituito, che specifica il tipo del valore calcolato e restituito dal metodo . Il tipo restituito di un metodo è void se non restituisce un valore.

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

La firma di un metodo deve essere univoca nell'ambito della classe in cui viene dichiarato il metodo. La firma di un metodo è costituita dal nome del metodo, dai tipi dei relativi parametri e/o dal numero dei relativi parametri. La firma di un metodo non include il tipo restituito.

Modificatori di visibilità dei metodi

Un metodo può avere uno dei due modificatori di visibilità facoltativi quando il metodo è presente in una classe derivata.

Il modificatore sottoponibile a override indica che questo metodo può essere sottoposto a override da un metodo (con lo stesso nome e firma) appartenente a una sottoclasse.

Il modificatore protetto indica che questo metodo è accessibile solo dai membri di una classe derivata successivamente.

Overload di un metodo

L'overload del metodo consente a più metodi nella stessa classe di avere lo stesso nome purché i relativi parametri differiscano in numero (in altre parole i metodi hanno un'arità diversa).

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

Nota

Tutti i metodi con lo stesso nome devono avere un'arità diversa. Ciò è dovuto al fatto che i linguaggi di programmazione tipizzato in modo debole non supportano l'overload in base al tipo.

Parametri

I parametri vengono usati per passare valori o riferimenti di variabili a un metodo. Un parametro descrive uno slot con un tipo e un nome e facoltativamente una parola chiave del modificatore. Un argomento è un valore effettivo passato in tale slot dal chiamante del metodo al chiamato.

I parametri di un metodo ottengono il valore dall'argomento specifico specificato quando viene richiamato il metodo. Il modo in cui gli argomenti vengono passati tra il chiamante e il chiamato dipende dal tipo del parametro. Per impostazione predefinita, tutti i parametri sono parametri di input, ovvero vengono sottoposto a marshalling dal chiamante solo al chiamato. Le parole chiave refdel modificatore , ref conste out possono essere aggiunte per modificare la direzione predefinita del marshalling tra il chiamante e il chiamato e creare parametri di output. Non tutte le parole chiave sono valide con tutti i tipi di parametro, tuttavia; le combinazioni valide sono descritte di seguito.

Importante

Common Language Runtime (CLR) include concetti e parole chiave di modifica che potrebbero sembrare simili a quelle descritte in questa sezione. Tuttavia, in pratica, tali modifiche non sono correlate e l'effetto di questi modificatori è specifico per la progettazione e il funzionamento del Windows Runtime.

I tipi valore sono parametri di input in modo implicito e per impostazione predefinita viene passata una copia dell'argomento dal chiamante al chiamato. I parametri valore possono essere trasformati in parametri di output con la out parola chiave. In tal caso, l'argomento viene sottoposto a marshalling invece dal chiamato solo al chiamante.

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

Come ottimizzazione delle prestazioni speciale, i tipi di struct (e nessun altro tipo), che in genere vengono passati per valore come copia completa, possono essere passati dal puntatore allo struct non modificabile. Questa operazione viene ottenuta con la ref const parola chiave (nonconst ref) che contrassegna il parametro struct come parametro di input, ma indica al gestore di marshalling di passare un puntatore alla risorsa di archiviazione dello struct, anziché passare una copia completa dello struct. Si noti tuttavia che lo struct non è modificabile; il puntatore è concettualmente un puntatore const. Non ci sono boxing coinvolti. Si tratta di una scelta pratica quando si accetta un valore di grandi dimensioni come Matrix4x4, ad esempio.

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

I tipi riferimento sono anche parametri di input impliciti, vale a dire che il chiamante è responsabile dell'allocazione dell'oggetto e del passaggio di un riferimento a esso come argomento; tuttavia, poiché l'argomento è un riferimento all'oggetto , le modifiche apportate all'oggetto dal chiamato vengono osservate dal chiamante dopo la chiamata. In alternativa, un tipo riferimento può essere reso un parametro di output con la out parola chiave . In tal caso i ruoli vengono invertiti; il chiamato è quello che alloca l'oggetto e lo restituisce al chiamante. Anche in questo caso, le ref parole chiave non possono essere usate in generale con i tipi riferimento (vedere l'eccezione riportata di seguito).

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

La tabella seguente riepiloga il comportamento delle parole chiave di marshalling per i parametri di valore e i parametri di riferimento:

Comportamento Allocato da Parola chiave Tipi Commenti
Parametro di input Chiamante (nessuna) Tutti i tipi Comportamento predefinito
ref const Solo Struct Ottimizzazione delle prestazioni
Parametro di output Chiamato out Tutti i tipi

Windows Runtime supporta i tipi di matrice, il cui comportamento come parametro è leggermente diverso. Una matrice è una struttura di dati che contiene una serie di variabili archiviate in sequenza e a cui si accede tramite un indice. Le variabili contenute in una matrice, denominate anche elementi della matrice, sono tutti dello stesso tipo e questo tipo viene chiamato tipo di elemento della matrice.

MIDL 3.0 supporta le dichiarazioni di una matrice unidimensionale.

Un parametro di matrice è un tipo riferimento e, come tutti i tipi riferimento, è per impostazione predefinita un parametro di input. In tal caso, il chiamante alloca la matrice al chiamato, che può leggere i relativi elementi, ma non modificarli (di sola lettura). Viene chiamato modello di matrice pass . In alternativa, il modello di matrice di riempimento può essere usato aggiungendo la ref parola chiave al parametro. In tale impostazione, la matrice viene ancora allocata dal chiamante, ma è concettualmente un parametro di output nel senso che il chiamato riempirà i valori degli elementi della matrice. Infine, l'ultimo modello è la matrice di ricezione in cui (come tutti i parametri di riferimento di output) il chiamato è allocare e inizializzare l'argomento prima che venga restituito al chiamante.

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

La tabella seguente riepiloga il comportamento per le matrici e i relativi elementi:

Modello di matrice Parola chiave Allocato da Accesso degli elementi tramite chiamato
"Pass array" (nessuna) Chiamante Sola lettura
"Matrice di riempimento" ref Chiamante Sola scrittura
"Receive array" out Chiamato Lettura/scrittura

Per altre info sull'uso dei parametri di matrice in stile C, noti anche come matrici conformi, con C++/WinRT, vedi Parametri di matrice.

Metodi statici e di istanza

Un metodo dichiarato con prefisso static modificatore è un metodo statico. Un metodo statico non ha accesso a un'istanza specifica e pertanto può accedere direttamente ad altri membri statici della classe.

Un metodo dichiarato senza un static modificatore è un metodo di istanza. Un metodo di istanza ha accesso a un'istanza specifica e può accedere sia ai membri statici che ai membri dell'istanza della classe .

La classe Entity seguente include sia membri statici che di istanza.

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

Ogni istanza di Entity contiene il proprio numero di serie (e presumibilmente alcune altre informazioni non visualizzate qui). Internamente, il costruttore Entity (simile a un metodo di istanza) inizializza la nuova istanza con il numero di serie disponibile successivo.

La proprietà SerialNo consente di accedere al numero di serie per l'istanza in cui si richiama il metodo get della proprietà .

I metodi statici GetNextSerialNo e SetNextSerialNo possono accedere al membro statico del numero di serie disponibile successivo interno della classe Entity .

Metodi sovrascrivibili e protetti

Tutti i metodi in un tipo di Windows Runtime sono effettivamente virtuali. Quando viene richiamato un metodo virtuale, il tipo in fase di esecuzione dell'istanza per cui viene eseguita la chiamata determina l'implementazione effettiva del metodo da richiamare.

Un metodo può essere sottoposto a override in una classe derivata. Quando una dichiarazione di metodo di istanza include un overridable modificatore, il metodo può essere sottoposto a override da classi derivate. Se una classe derivata esegue effettivamente l'override di un metodo di classe base sostituibile è determinato dall'implementazione; non è presente nei metadati. Se una classe derivata redeclares un metodo nella classe base, dichiara un nuovo metodo che si trova insieme al metodo della classe derivata, anziché eseguirne l'override.

Quando una dichiarazione di metodo di istanza include un protected modificatore, il metodo è visibile solo alle classi derivate.

Eventi

Una dichiarazione di evento è un membro che specifica che una classe è un'origine evento. Tale origine evento fornisce notifiche a qualsiasi destinatario che implementa un delegato (un metodo con una firma specifica).

Si dichiara un evento usando la event parola chiave , seguito dal nome del tipo delegato (che descrive la firma del metodo richiesta), seguito dal nome dell'evento. Ecco un evento di esempio che usa un tipo delegato esistente dalla piattaforma.

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

Una dichiarazione di evento aggiunge implicitamente due metodi alla classe : un metodo add , che un client chiama per aggiungere un gestore eventi all'origine e un metodo remove , che un client chiama per rimuovere un gestore eventi aggiunto in precedenza. Ecco altri esempi.

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

Per convenzione, due parametri vengono sempre passati a un gestore eventi Windows Runtime: l'identità del mittente e un oggetto argomenti evento. Il mittente è l'oggetto che ha generato l'evento o Null per gli eventi statici. Se l'evento non ha payload significativo, gli argomenti dell'evento sono object il cui valore è Null.

Delegati

Un tipo delegato specifica un metodo con un particolare elenco di parametri e un tipo restituito. Una singola istanza di un evento può contenere un numero qualsiasi di riferimenti alle istanze del tipo delegato. La dichiarazione è simile a quella di un metodo membro normale, ad eccezione del fatto che esiste all'esterno di una classe di runtime e ha il prefisso della delegate parola chiave .

Un delegato consente di trattare i metodi come entità che possono essere assegnate alle variabili e passate come parametri. I delegati sono simili al concetto di puntatori a funzione presenti in altri linguaggi. Tuttavia, a differenza dei puntatori a funzione, i delegati sono orientati agli oggetti e indipendenti dai tipi.

Se non si vuole usare il tipo di delegato WindowSizeChangedEventHandler dalla piattaforma, è possibile definire il proprio tipo delegato.

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

Un'istanza del tipo delegato SizeChangedHandler può fare riferimento a qualsiasi metodo che accetta due argomenti (object eWindowSizeChangedEventArgs) e restituisce void. Dopo aver discusso gli struct, sarà anche possibile sostituire il parametro WindowSizeChangedEventArgs con un tipo di argomenti di evento personalizzato.

Una proprietà interessante e utile di un delegato è che non conosce o si preoccupa della classe del metodo a cui fa riferimento; è importante che il metodo a cui si fa riferimento abbia gli stessi parametri e il tipo restituito del delegato.

Facoltativamente, è possibile attribuire una dichiarazione di delegato con [uuid(...)].

Vedere anche Delegati che restituiscono HRESULT.

Struct

Uno struct è una struttura di dati che può contenere membri dati (campi). A differenza di una classe, tuttavia, uno struct è un tipo valore.

I tipi struct sono particolarmente utili per strutture dati di piccole dimensioni che hanno una semantica di valori. I numeri complessi o i punti in un sistema di coordinate sono esempi validi di struct. L'uso di struct anziché di classi per strutture di dati di piccole dimensioni può fare una grande differenza nel numero di allocazioni di memoria eseguite da un'applicazione.

Si userà un esempio per confrontare classi e struct. Ecco una versione di Point first come classe.

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

Questo programma C# crea e inizializza una matrice di 100 istanze di Point. Con Point implementato come classe, vengono create istanze di 101 oggetti separati: uno per l'oggetto matrice stesso; e uno per ognuno degli elementi Point 100.

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

Un'alternativa più efficiente consiste nel rendere Point uno struct, anziché una classe .

struct Point
{
    Int32 x;
    Int32 y;
};

Viene ora creata un'istanza di un solo oggetto, ovvero l'oggetto matrice stesso. Gli elementi Point vengono archiviati in linea all'interno della matrice; una disposizione di memoria che le cache del processore sono in grado di usare per un effetto potente.

La modifica di uno struct è una modifica binaria che causa un'interruzione binaria. Pertanto, gli struct implementati come parte di Windows stesso non vengono modificati una volta introdotti.

Interfacce

Un'interfaccia definisce un contratto che può essere implementato dalle classi. Un'interfaccia può contenere metodi, proprietà ed eventi, proprio come le classi.

Diversamente da una classe, un'interfaccia non fornisce implementazioni dei membri definiti. Specifica semplicemente i membri che devono essere forniti da qualsiasi classe che implementa l'interfaccia .

Le interfacce possono richiedere una classe che implementa l'interfaccia per implementare anche altre interfacce. Nell'esempio seguente l'interfaccia IComboBox richiede che qualsiasi classe che implementi IComboBox implementi anche ITextBox e IListBox. Inoltre, una classe che implementa IComboBox deve implementare anche IControl. Questo perché sia ITextBox che IListBoxlo richiedono.

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

Una classe può implementare zero o più interfacce. Nell'esempio seguente la classe EditBox implementa sia IControl che IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Per Windows Runtime tipi nella piattaforma Windows, viene definita un'interfaccia se gli sviluppatori che utilizzano tali tipi devono implementare l'interfaccia. Un altro caso d'uso per definire un'interfaccia è quando più classi di runtime implementano l'interfaccia e gli sviluppatori che utilizzano tali classi di runtime accederanno a tipi diversi di oggetti in modo generico (e quindi polimorficamente) tramite tale interfaccia comune.

Nota

Si pensi due volte all'uso della requires parola chiave in MIDL 3.0. Può portare a disegni disordinati, soprattutto quando viene preso in considerazione il controllo delle versioni.

Enumerazioni

Un tipo enum (o tipo enumerato o enumerazione) è un tipo valore distinto con un set di costanti denominate. L'esempio seguente definisce e usa un tipo di enumerazione denominato Color con tre valori costanti: Red, Green e Blue.

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

Ogni tipo enum ha un tipo integrale corrispondente denominato tipo sottostante del tipo enum. Il tipo sottostante di un'enumerazione è Int32 o UInt32.

Il Windows Runtime supporta due tipi di enumerazioni: enumerazioni normali e flag. Un'enumerazione del tipo normale esprime un set di valori esclusivi; mentre uno dei tipi flag rappresenta un set di valori booleani. Per abilitare gli operatori bit per bit per un'enumerazione flag, il compilatore MIDL 3.0 genera overload degli operatori C++.

Un'enumerazione flags ha l'attributo [flags] applicato. In tal caso, il tipo sottostante dell'enumerazione è UInt32. Quando l'attributo [flags] non è presente (un'enumerazione normale), il tipo sottostante dell'enumerazione è Int32. Non è possibile dichiarare un'enumerazione come qualsiasi altro tipo.

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

Il formato di archiviazione e l'intervallo di valori possibili di un tipo enum sono determinati dal tipo sottostante. Il set di valori che un tipo enum può accettare non è limitato dai membri enum dichiarati.

L'esempio seguente definisce un tipo di enumerazione denominato Alignment, con un tipo sottostante di Int32.

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

Come vale anche per C e C++, un'enumerazione MIDL 3.0 può includere un'espressione costante che specifica il valore del membro (come illustrato in precedenza). Il valore costante per ogni membro enumerazione deve essere compreso nell'intervallo del tipo sottostante dell'enumerazione. Quando una dichiarazione di membro enum non specifica in modo esplicito un valore, al membro viene assegnato il valore zero (se è il primo membro nel tipo enum) o al valore del membro enumerazione precedente a testo più uno.

L'esempio seguente definisce un tipo di enumerazione denominato Permissions, con un tipo sottostante di UInt32.

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

Attributi

Tipi, membri e altre entità nel codice sorgente MIDL 3.0 supportano modificatori che controllano determinati aspetti del comportamento. Ad esempio, l'accessibilità di un metodo viene controllata usando il protected modificatore di accesso. MIDL 3.0 generalizza questa funzionalità in modo che i tipi definiti dall'utente di informazioni dichiarative possano essere collegati alle entità del programma e recuperati in fase di esecuzione dai metadati.

I programmi specificano queste informazioni dichiarative aggiuntive definendo e usando attributi.

L'esempio seguente definisce un attributo HelpAttribute , che può essere inserito nelle entità del programma per fornire collegamenti alla relativa documentazione associata. Come si può notare, un attributo è essenzialmente un tipo di struct, quindi non ha un costruttore e contiene solo membri dati.

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

Un attributo può essere applicato assegnando il nome, insieme a qualsiasi argomento, racchiuso tra parentesi quadre appena prima della dichiarazione associata. Se il nome di un attributo termina con Attribute, tale parte del nome può essere omessa quando viene fatto riferimento all'attributo. Ad esempio, l'attributo HelpAttribute può essere usato in questo modo.

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

È possibile applicare lo stesso attributo a più dichiarazioni usando un blocco di ambito che segue l'attributo . Ovvero, un attributo immediatamente seguito da parentesi graffe che circondano le dichiarazioni a cui si applica l'attributo .

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

Gli attributi implementati come parte di Windows sono in genere nello spazio dei nomi Windows.Foundation .

Come illustrato nel primo esempio, si usa l'attributo nella definizione dell'attributo [attributeusage(<target>)] . I valori di destinazione validi sono target_all, , target_eventtarget_delegatetarget_enum, target_field, target_interface, , target_propertytarget_parametertarget_methodtarget_runtimeclass, , e .target_struct È possibile includere più destinazioni tra parentesi, separate da virgole.

Altri attributi che è possibile applicare a un attributo sono [allowmultiple] e [attributename("<name>")].

Tipi con parametri

L'esempio seguente genera l'errore MIDL2025: [msg]errore di sintassi [context]: previsto > o vicino a ">>".

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

Inserire invece uno spazio tra i due > caratteri in modo che la coppia di caratteri di chiusura del modello non venga interpretata erroneamente come operatore di spostamento a destra.

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

L'esempio seguente genera l'errore MIDL2025: [msg]errore di sintassi [context]: previsto > o vicino a "[". Ciò è dovuto al fatto che non è valido usare una matrice come argomento di tipo di parametro per un'interfaccia con parametri.

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

Per la soluzione, vedere Restituzione di una matrice in modo asincrono.