Introduzione a Microsoft Interface Definition Language 3.0
Microsoft Interface Definition Language (MIDL) 3.0 è una sintassi moderna semplificata per la definizione dei tipi Windows Runtime all'interno dei file IDL (Interface Definition Language) ( file.idl
). Questa nuova sintassi sarà familiare a chiunque abbia familiarità con C, C++, C# e/o Java. MIDL 3.0 è un modo particolarmente pratico per definire 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; In questo esempio viene illustrata 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 definizione di 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, usato con l'opzione /winrt
).
Nota
Vedere anche il riferimento consolidato di Windows Runtime (Il sistema dei tipi di Windows Runtimee i file di metadati di Windows).
Interface Definition Language (IDL) ha iniziato con il sistema Distributed Computing Environment/Remote Procedure Calls (DCE/RPC). Il 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 di Windows Runtime per la piattaforma Windows. Se guardi nella cartella windows SDK %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt
vedrai esempi di file .idl
scritti con la sintassi MIDL 2.0. Si tratta di API Windows Runtime predefinite, dichiarate nel formato ABI (Application Binary Interface). Questi file esistono principalmente per gli strumenti da usare: non si creano né si usano queste API in questo formato (a meno che non si stia scrivendo codice di basso livello).
Vedere anche Transition to MIDL 3.0 from classic MIDLRT.
MIDL 3.0 è una sintassi molto più semplice e moderna, il cui scopo è dichiarare le API di Windows Runtime. Ed è possibile usarlo nei progetti, in particolare per definire classi di runtime di C++/WinRT. Le intestazioni, per l'uso da C++/WinRT, per le API di Windows Runtime predefinite fanno parte dell'SDK, all'interno della cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt
.
In generale, tutte le API di Windows Runtime sono progettate per essere disponibili per tutte le proiezioni del linguaggio di Windows Runtime. Questa operazione viene eseguita, in parte, scegliendo di passare esclusivamente i tipi di Windows Runtime da e verso le API di Windows Runtime. Sebbene si tratti di una decisione di progettazione valida di passare un'interfaccia COM non elaborata da e verso un'API di Windows Runtime, in questo modo i consumer di quel particolare API Windows Runtime alle applicazioni C++. La tecnica può essere vista in scenari di interoperabilità, ad esempio durante l'interoperabilità tra Direct3D e XAML. Poiché Direct3D è nell'immagine, lo scenario è necessariamente limitato alle applicazioni C++. Pertanto, 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 a 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 questo motivo. Queste eccezioni correlate all'interoperabilità si trovano in intestazioni di interoperabilità, ad esempio windows.ui.xaml.media.dxinterop.h
.
Se sono funzionalità o funzionalità di un componente COM che si desidera esporre alle proiezioni del linguaggio Windows Runtime oltre C++, è possibile creare un componente C++ componente Windows Runtime (WRC) che crea e usa direttamente il componente COM (ad esempio DirectX) ed espone una replica di alcuni subset delle relative funzionalità e funzionalità sotto forma di una superficie dell'API di Windows Runtime che accetta e restituisce Windows Solo tipi di runtime. È quindi possibile utilizzare tale WRC da un'applicazione scritta in qualsiasi proiezione del linguaggio di Windows Runtime.
I concetti principali dell'organizzazione in una definizione MIDL 3.0 sono spazi dei nomi, tipi e membri. Un file di origine MIDL 3.0 (un file .idl
) 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.
- Classi, interfacce, strutture ed enumerazioni sono tipi.
- Metodi, proprietà, eventi e campi sono esempi di membri.
Quando si compila un file di origine MIDL 3.0, il compilatore (midl.exe
) genera un file di metadati di Windows Runtime (in genere un file di .winmd
).
// 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 lo spazio dei nomi.
Questa classe implementa l'interfaccia Windows.UI.Xaml.Data.INotifyPropertyChanged
Suggerimento
Visual Studio offre l'esperienza migliore per la compilazione di MIDL 3.0, tramite l'estensione visual studio C++/WinRT (VSIX). Vedere supporto di Visual Studio per C++/WinRT e VSIX.
Ma è 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 dell'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 strumento midl.exe
compila l'esempio e produce un file di metadati denominato Bookstore.winmd
(per impostazione predefinita viene usato il nome del file .idl
).
Suggerimento
Se si usano più file IDL (per consigli su questo, vedere classi di runtime di Factoring in file Midl (con estensione idl)), unire tutti i file .winmd
risultanti in un singolo file con lo stesso nome dello spazio dei nomi radice. Tale .winmd
file finale sarà quello a cui faranno riferimento i consumer delle API.
In questo caso,
È anche possibile usare il comando where
per scoprire dove è installato midl.exe
.
where midl
Se si desidera utilizzare i tipi definiti in un file .idl
da un file di .idl
diverso, usare la direttiva import
. Per altri dettagli e un esempio di codice, vedi controlli XAML; eseguire l'associazione a una proprietà C++/WinRT. Naturalmente, se si utilizza un componente predefinito o di terze parti, non si avrà accesso al file .idl
. Ad esempio, è possibile usare l'API Win2D Win2D windows Runtime per il rendering della grafica 2D in modalità immediata. Il comando precedente ha usato l'opzione /reference
per fare riferimento a un file di metadati di Windows Runtime (.winmd
). In questo esempio successivo si userà di nuovo tale opzione, immaginando lo scenario in cui è Bookstore.winmd
, ma non Bookstore.idl
.
// MVVMApp.idl
namespace MVVMApp
{
runtimeclass ViewModel
{
ViewModel();
Bookstore.BookSku BookSku{ get; };
}
}
Se il codice sorgente per l'esempio precedente viene archiviato in un file denominato MVVMApp.idl
, è possibile eseguire il comando seguente per fare riferimento a Bookstore.winmd
.
midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" /reference Bookstore.winmd MVVMApp.idl
È necessario uno spazio dei nomi. Antepone 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 di spazio dei nomi subordinato. Il nome dei tipi definiti in un ambito dello spazio dei nomi subordinato ha un prefisso di tutti i nomi degli spazi dei nomi contenenti.
Gli esempi seguenti sono due modi per dichiarare la stessa classe Windows.Foundation.Uri (come si può notare, i punti separano i livelli degli 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 gli 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 aprirne uno nuovo, come questo.
namespace RootNs.SubNs1
{
runtimeclass MySubNs1Class
{
void DoWork();
}
}
namespace RootNs.SubNs1.SubNs2
{
runtimeclass MySubNs2Class
{
void DoWork();
}
}
Esistono due tipi di dati in MIDL 3.0: tipi valore e tipi riferimento. Una variabile di un tipo valore contiene direttamente i dati. Una variabile di un tipo riferimento archivia un riferimento ai relativi dati( tale variabile è nota anche come oggetto ).
È possibile che due variabili di tipo riferimento facciano riferimento allo stesso oggetto. Pertanto, un'operazione su una variabile influisce sull'oggetto a cui fa riferimento l'altra variabile. Con i tipi valore, le variabili hanno una propria copia dei dati e non è possibile che un'operazione su uno influisca sull'altra.
I tipi valore di MIDL 3.0 sono ulteriormente suddivisi in tipi semplici, tipi enumerazione, tipi struct e tipi nullable.
I tipi riferimento MIDL 3.0 sono ulteriormente suddivisi in tipi di classe, tipi di interfaccia e tipi delegati.
Ecco una panoramica del sistema di tipi MIDL 3.0. A differenza delle versioni precedenti di MIDL, non è possibile usare alias per questi tipi.
Categoria | Descrizione | |
---|---|---|
Tipi valore | Tipi semplici | Integrale con segno: Int16, Int32, Int64 |
Integrale senza segno: UInt8, UInt16, UInt32, UInt64 | ||
Caratteri Unicode: char (rappresenta un UTF-16LE; un'unità di codice Unicode a 16 bit) | ||
Stringhe Unicode: stringhe | ||
Virgola mobile IEEE: Single, Double | ||
Boolean: booleano | ||
UUID a 128 bit: Guid | ||
Tipi di enumerazione | Tipi definiti dall'utente del modulo enumerazione E {...} | |
Tipi di struct | Tipi definiti dall'utente del modulo struct S {...} | |
Tipi nullable | Estensioni di tutti gli altri tipi valore con un valore null |
|
Tipi di riferimento | Tipi di classe | Classe base ultimate di tutti gli altri tipi: Object |
Tipi definiti dall'utente del modulo runtimeclass C {...} | ||
Tipi di interfaccia | Tipi definiti dall'utente dell'interfaccia modulo I {...} | |
Tipi delegati | Tipi definiti dall'utente del modulo delegato <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 e Double, rappresentano i dati usando rispettivamente i formati IEEE 754 a precisione singola a 32 bit e a 64 bit.
Il tipo booleano
Caratteri e stringhe in MIDL 3.0 contengono caratteri Unicode. Il tipo Char
La tabella seguente riepiloga i tipi numerici midl 3.0.
Categoria | Bit | Digitare | Intervallo/precisione |
---|---|---|---|
Integrale con segno | 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 | |
Integrale senza segno | 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 | single | 1,5 × 10−45 a 3,4 × 1038, precisione a 7 cifre |
64 | double | Da 5,0 × 10−324 a 1,7 × 10308, precisione a 15 cifre |
I file di origine MIDL 3.0 usano definizioni dei tipi 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 runtimeclass,
- tipi delegati e
- tipi di enumerazione.
Un attributo tipo definisce un attributo di 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 di Windows Runtime che contiene membri dati (campi). Gli struct sono tipi 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'interfaccia tipo definisce un'interfaccia di Windows Runtime, ovvero un set denominato di membri della funzione. Un'interfaccia può specificare che un'implementazione dell'interfaccia deve implementare anche una o più interfacce aggiuntive (obbligatorie) specificate. Ogni tipo di interfaccia deriva direttamente dall'interfaccia IInspectable di 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 delegato tipo definisce un delegato di Windows Runtime, che rappresenta un riferimento a un metodo con un particolare elenco di parametri e un tipo restituito. I delegati consentono di considerare un metodo come un'entità che può essere passata come parametro. Un delegato è simile al concetto di puntatore a funzione trovato in altri linguaggi. A differenza dei puntatori a funzione, i delegati sono orientati agli oggetti e indipendenti dai tipi.
Un tipo di 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 corrisponde al set di valori del tipo sottostante.
MIDL 3.0 supporta tre categorie di tipi aggiuntive.
- tipi di matrice unidimensionale,
- Tipi valore nullable e
- tipo di object
.
Non è necessario dichiarare una matrice unidimensionale prima di poterla usare. I tipi di matrice vengono invece costruiti seguendo un nome di tipo con parentesi quadre. Ad esempio, Int32[] è una matrice unidimensionale di Int32.
Analogamente, non è necessario definire tipi valore nullable prima di poterli usare. Per ogni tipo di valore non nullable T (ad eccezione di String), esiste un tipo nullable corrispondente Windows.Foundation.IReference<T>, che può contenere il valore aggiuntivo null
. Ad esempio, Windows.Foundation.IReference<Int32> è un tipo che può contenere qualsiasi numero intero a 32 bit o il valore null
. Vedere anche IReference<T>.
Infine, MIDL 3.0 supporta il tipo di Object
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 di . Gli operatori in un'espressione indicano quali operazioni applicare agli operandi. Esempi di operatori includono +, -, *, /e new
. Esempi di operandi includono valori letterali, campi, variabili locali ed espressioni.
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 maggiore rispetto all'operatore + . Le operazioni logiche hanno una precedenza inferiore rispetto alle operazioni bit per bit.
La tabella seguente riepiloga gli operatori MIDL 3.0, elencando le categorie di operatori in ordine di precedenza dal più alto al più basso. Gli operatori nella stessa categoria hanno la stessa precedenza.
Categoria | Espressione | Descrizione |
---|---|---|
Primario | x++ | Incremento successivo |
x-- | Post-decremento | |
Unario | +x | Identità |
-x | Negazione | |
!x | Negazione logica | |
~x | Negazione bit per bit | |
++x | Pre-incremento | |
--x | Pre-decremento | |
Moltiplicativo | x * y | Moltiplicazione |
x/ y | Divisione | |
x % y | Resto | |
Additivo | x + y | Addizione, concatenazione di stringhe, combinazione di delegati |
x – y | Sottrazione, rimozione del delegato | |
Spostare | x << y | Maiusc a sinistra |
x >> y | Sposta a destra | |
AND bit per bit | x & y | AND bit per bit intero |
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 (o classi di runtime) sono le classi più fondamentali dei tipi MIDL 3.0. Una classe è una definizione di un'aggregazione di metodi, proprietà ed eventi in una singola unità. Le classi supportano ereditarietà e polimorfismo, ovvero meccanismi in cui classi derivate possono estendere ed specializzare classi base.
Si definisce un nuovo tipo di classe usando una definizione di classe. Una definizione di classe inizia con un'intestazione che specifica la parola chiave runtimeclass
, 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; };
}
Definisce una nuova classe di Windows Runtime denominata
Per impostazione predefinita, una classe runtime è sealed e la derivazione da essa non è consentita. Vedere classi base.
Per associare XAML a un modello di visualizzazione, è necessario definire la classe di runtime del modello di visualizzazione in MIDL. Vedi controlli XAML; associare a una proprietà C++/WinRT per altri dettagli.
È possibile dichiarare che una classe non supporta istanze (e di conseguenza deve contenere solo membri statici) anteponendo la definizione della classe di runtime con la parola chiave static
. 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 anteponendo la definizione della classe di runtime con la parola chiave partial
. 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 classi parziali vengono generate dal computer.
Modificatore | Significato |
---|---|
statico | La classe non ha istanze. Di conseguenza, sono consentiti solo i membri statici. |
parziale | La definizione della classe è incompleta. |
Vedi composizione e di attivazione per modificatori avanzati.
Poiché MIDL 3.0 è un linguaggio di definizione per descrivere la superficie pubblica dei tipi 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 di public
(con ridondanza efficace).
Una definizione di classe può specificare una classe base seguendo il nome della classe e i parametri di tipo con due punti e il nome della classe base. L'omissione di una specifica della classe di base equivale alla derivazione 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. Esistono vincoli per le classi componibili. Affinché un'applicazione superi il kit di certificazione delle app Windows test usati da Visual Studio e da Microsoft Store per convalidare gli invii (e pertanto affinché l'applicazione venga inserita correttamente in Microsoft Store), una classe componibile deve in ultima analisi derivare da una classe di base di Windows. Ciò significa che la classe nella radice della gerarchia di ereditarietà deve essere un tipo che ha origine in uno spazio dei nomi Windows.*.
Vedi controlli XAML; associare a una proprietà C++/WinRT per altri dettagli.
Nell'esempio seguente, la classe base di Volume è Areae la classe 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 sono definiti nello stesso file di origine. Per una descrizione dei vantaggi e dei svantaggi, vedere classi di runtime di Factoring in 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 che eredita, ma non può rimuovere la definizione di un membro ereditato.
Nell'esempio precedente
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.
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
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 sull'implementazione effettiva.
I membri di una classe sono membri statici o membri dell'istanza di . 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 eseguite da un'istanza della classe o dalla classe stessa |
Avvenimenti | Notifiche che possono essere generate da un'istanza della classe |
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
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 dispone di 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 si dispone di un metodo factory altrove che restituisce un'istanza della classe .
L'eccezione è classi non sealed. Una classe non sealed può avere uno o più costruttori protetti.
Proprietà sono concettualmente simili ai campi ,ad esempio campi C# o campi di uno struct MIDL 3.0. Sia le proprietà che 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à hanno invece funzioni di accesso che specificano la funzione da eseguire quando si legge o si scrive una proprietà.
Una proprietà viene dichiarata come campo di uno struct, ad eccezione del fatto che la dichiarazione termina con una parola chiave get
e/o una parola chiave set
scritta tra i delimitatori { e }e termina con un punto e virgola.
Una proprietà con una parola chiave get
e una parola chiave set
è una proprietà di lettura/scrittura. Una proprietà con solo una parola chiave get
è una proprietà di sola lettura . Windows Runtime non supporta le proprietà di sola scrittura.
Ad esempio, la classe
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 parole chiave get
e set
. L'omissione implica che la proprietà è di lettura/scrittura ed è semanticamente identica a fornire le parole chiave get
e set
in tale ordine,get
, seguita da set
.
Inoltre, è possibile specificare solo la parola chiave get
per indicare che la proprietà è di sola lettura.
// Read-only instance property returning mutable collection.
Windows.Foundation.Collections.IVector<Windows.UI.Color> Colors { get; };
Windows Runtime non supporta le proprietà di sola scrittura. È tuttavia possibile specificare solo la parola chiave set
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à
unsealed runtimeclass Area
{
...
Color SurfaceColor { get; set; };
}
Se, invece, è
In tal caso, aggiungere la proprietà set
parola chiave 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 di sola lettura, l'aggiunta della parola chiave set non dichiara una proprietà di sola scrittura, ma una proprietà di lettura/scrittura.
L'implementazione di Windows Runtime di una proprietà è uno o due metodi 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 funzione di accesso get
corrisponde a un metodo senza parametri con un valore restituito del tipo di proprietà, ovvero il getter della proprietà.
Una funzione di accesso set
corrisponde a un metodo con un singolo parametro denominato valoree 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; };
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 static
e le proprietà dell'istanza vengono dichiarate senza di essa.
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 dispone inoltre di 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);
Il firma di un metodo deve essere univoco nella 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.
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.
Il metodo l'overload 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 arity diverso).
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 differenze arità. Ciò è dovuto al fatto che i linguaggi di programmazione tipizzato in modo debole non supportano l'overload in base al tipo.
Parametri vengono usati per passare valori o riferimenti a 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 del modificatore ref
, ref const
e 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, questi non sono correlati e l'effetto di questi modificatori è specifico per la progettazione e il funzionamento di Windows Runtime.
I tipi valore sono implicitamente parametri di inpute 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 parola chiave out
; in tal caso, l'argomento viene sottoposto a marshalling solo dal chiamato 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 normalmente vengono passati per valore come copia completa, possono essere eseguiti per essere passati dal puntatore allo struct non modificabile. Questo risultato viene ottenuto con la parola chiave ref const
(nonconst ref
), che contrassegna il parametro struct come parametro di input, ma indica al gestore di marshalling di passare un puntatore all'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 c'è alcun boxing coinvolto. Si tratta di una scelta pratica quando si accetta un valore di grandi dimensioni come un 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 a tale oggetto dal chiamato vengono osservate dal chiamante dopo la chiamata. In alternativa, un tipo riferimento può essere creato un parametro di output con la parola chiave out
. In tal caso i ruoli vengono invertiti; il chiamato è quello che alloca l'oggetto e lo restituisce al chiamante. Anche in questo caso, le parole chiave ref
non possono essere usate in generale con i tipi di riferimento (vedere l'eccezione seguente).
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 | Osservazioni |
---|---|---|---|---|
Parametro di input | Chiamante | (nessuno) | 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 modo sequenziale e accessibili 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 per impostazione predefinita, tutti i tipi riferimento sono un parametro di input. In tal caso, il chiamante alloca la matrice al chiamato, che può leggere i relativi elementi, ma non modificarli (sola lettura). Questa operazione viene chiamata ref
al parametro; in tale configurazione, 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 è il ricevere matrice dove (come tutti i parametri di riferimento di output) il chiamato è allocato e inizializzato 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 |
---|---|---|---|
"Passa matrice" | (nessuno) | Chiamante | Sola lettura |
"Matrice di riempimento" | ref |
Chiamante | Solo scrittura |
"Matrice di ricezione" | 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 .
Un metodo dichiarato con un modificatore static
con prefisso è 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 modificatore di static
è 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 .
Nella classe Entity
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
La proprietà SerialNo consente di accedere al numero di serie per l'istanza in cui viene richiamata la proprietà get metodo.
I metodi statici GetNextSerialNo e SetNextSerialNo possono accedere al numero di serie interno successivo numero di serie disponibile membro statico della classe Entity.
Tutti i metodi in un tipo Windows Runtime sono effettivamente virtuali. Quando viene richiamato un metodo virtuale, il tipo di runtime dell'istanza per cui viene eseguita tale chiamata determina l'implementazione effettiva del metodo da richiamare.
Un metodo può essere sottoposto a override in una classe derivata. Quando una dichiarazione del metodo di istanza include un modificatore overridable
, 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 è determinata 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 del metodo di istanza include un modificatore protected
, il metodo è visibile solo alle classi derivate.
Una dichiarazione di evento
Si dichiara un evento usando la parola chiave event
, seguita 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 in modo implicito due metodi alla classe: un aggiungere metodo, che un client chiama per aggiungere un gestore eventi all'origine e un rimuovere metodo, 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 di 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 un Object il cui valore è Null.
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 ed è preceduta dalla parola chiave delegate
.
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. A differenza dei puntatori a funzione, tuttavia, i delegati sono orientati agli oggetti e indipendenti dai tipi.
Se non si vuole usare il WindowSizeChangedEventHandler tipo delegato 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 (un Objecte un WindowSizeChangedEventArgs) e restituisce void. Dopo aver discusso struct, sarà anche possibile sostituire il parametro WindowSizeChangedEventArg s con un argomenti evento tipo 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.
Un struct
Gli struct sono particolarmente utili per piccole strutture di dati con semantica di valore. Numeri complessi, o punti in un sistema di coordinate, sono esempi validi di struct. L'uso di struct anziché 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 prima 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 dei 100 elementi Point.
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
La modifica di uno struct è una modifica che causa un'interruzione binaria. Pertanto, gli struct implementati come parte di Windows stessa non vengono modificati una volta introdotti.
Un'interfaccia definisce un contratto che può essere implementato dalle classi. Un'interfaccia può contenere metodi, proprietà ed eventi, proprio come le classi.
A differenza di 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 richiedono 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. Ciò è dovuto al fatto che sia ITextBox che IListBox 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 i tipi di Windows Runtime 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
Considerare due volte l'uso della parola chiave requires
in MIDL 3.0. Può portare a disegni disordinati, soprattutto quando il controllo delle versioni viene preso in considerazione.
Un tipo enumerazione (o tipo enumerato o enumerazione) è un tipo valore distinto con un set di costanti denominate. Nell'esempio seguente viene definito e usato un tipo enumerazione denominato Color con tre valori costanti: Red, Greene 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.
Windows Runtime supporta due tipi di enumerazioni: enumerazioni di normali e flag enumerazioni. Un'enumerazione del tipo normale esprime un insieme di valori esclusivi; mentre uno dei tipi di 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++.
A un'enumerazione flags è applicato l'attributo [flags]
. 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 enumerazione sono determinati dal tipo sottostante. Il set di valori che un tipo di enumerazione può assumere non è limitato dai membri di enumerazione dichiarati.
Nell'esempio seguente viene definito un tipo enumerazione denominato Alignment, con un tipo sottostante di Int32.
enum Alignment
{
Left = -1,
Center = 0,
Right = 1
};
Come è anche vero 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 al testo più uno.
Nell'esempio seguente viene definito un tipo di enumerazione denominato Permissions, con un tipo sottostante di UInt32.
[flags]
enum Permissions
{
None = 0x0000,
Camera = 0x0001,
Microphone = 0x0002
};
Tipi, membri e altre entità nel codice sorgente MIDL 3.0 supportano modificatori che controllano determinati aspetti del loro comportamento. Ad esempio, l'accessibilità di un metodo viene controllata usando il modificatore di accesso protected
. 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 gli attributi .
Nell'esempio seguente viene definito un attributo HelpAttribute, che può essere inserito nelle entità del programma per fornire collegamenti alla 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, all'interno di parentesi quadre appena prima della dichiarazione associata. Se il nome di un attributo termina in Attributo, 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 [attributeusage(<target>)]
nella definizione dell'attributo. I valori di destinazione validi sono target_all
, target_delegate
, target_enum
, target_event
, target_field
, target_interface
, target_method
, target_parameter
, target_property
, target_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>")]
.
L'esempio seguente genera errore MIDL2025: [msg]errore di sintassi [contesto]: 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 errore MIDL2025: [msg]errore di sintassi [context]: previsto > o, vicino a "[". Ciò è dovuto al fatto che non è valido usare una matrice come argomento del tipo di parametro per un'interfaccia con parametri.
Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();
Per la soluzione, vedere Restituzione di una matrice in modo asincrono.