Attributi predefiniti (MIDL 3.0)
Sono disponibili diversi attributi personalizzati predefiniti che consentono di controllare il nome e l'identificatore di interfaccia (IID) per le interfacce sintetizzate dal compilatore. Questi attributi consentono di controllare il controllo delle versioni e l'API binaria della classe a un livello granulare.
Se si è uno sviluppatore di componenti e/o un autore di librerie, è possibile usare questi attributi per assicurarsi che i componenti rimangano binari stabili da una versione a quella successiva.
Se si è uno sviluppatore di applicazioni, in generale non sarà necessario usare questi attributi perché l'applicazione verrà ricompilata dopo la revisione dei tipi.
Per informazioni dettagliate sull'uso e lo scopo dell'attributo allowforweb
, vedere la classe AllowForWebAttribute.
L'attributo constructor_name
specifica il nome e l'IID dell'interfaccia factory che contiene i membri del costruttore. Per altre informazioni sulle interfacce factory, vedere Sintetizzare le interfacce.
Nota
Un'interfaccia factory è applicabile solo a una classe sealed con costruttori non predefiniti o a una classe non sealed con costruttori predefiniti e/o non predefiniti.
Nell'esempio seguente il costruttore Block protetto viene inserito nell'interfaccia IBlockFactory e tale interfaccia ha l'IID specificato.
[constructor_name("Windows.UI.Xaml.Documents.IBlockFactory", 07110532-4f59-4f3b-9ce5-25784c430507)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
...
protected Block();
...
}
Di conseguenza, quando la descrizione della classe non fa altrimenti riferimento a un'interfaccia, ma quando ne è necessaria una per implementare la classe, il compilatore MIDL 3.0 sintetizza e aggiunge le interfacce, se necessario.
l'attributo contentproperty
rappresenta la classe ContentPropertyAttribute . Ecco un esempio:
// BgLabelControl.idl
namespace BgLabelControlApp
{
[contentproperty("Content")]
runtimeclass BgLabelControl : Windows.UI.Xaml.Controls.Control
{
BgLabelControl();
static Windows.UI.Xaml.DependencyProperty LabelProperty{ get; };
String Label;
static Windows.UI.Xaml.DependencyProperty ContentProperty{ get; };
IInspectable Content;
}
}
Non usare l'attributo contract
nelle PROPRIE API, ma solo per le API Windows incorporate.
contract
L'attributo specifica il nome e la versione del contratto api Windows 10 (vedere Programmazione con SDK di estensione) in cui il tipo con attributi e/o il membro sono stati introdotti per la prima volta in Windows (pertanto non è significativo per le API non recapitate come parte di Windows). L'attributo assume il formato [contract(ContractName, ContractVersion)]
e viene visualizzato prima dell'elemento a cui si applica.
Se non si specifica un'interfaccia predefinita, il compilatore MIDL 3.0 sceglie l'interfaccia della prima istanza. Per eseguire l'override di questa selezione, inserire l'attributo default
prima dell'interfaccia che si vuole impostare come interfaccia predefinita.
// Declaring an external interface as the default
runtimeclass C : [default]I { ... }
// Declaring a specific exclusiveto interface as the default.
// This is very unusual.
runtimeclass C
{
...
[default][interface_name(...)]
{
...
}
}
L'attributo default_interface
viene usato per forzare la generazione di un'interfaccia predefinita in cui non verrebbe generata in caso contrario. Nell'esempio seguente , StateTriggerBase non ha bisogno di un'interfaccia predefinita (perché non ha membri non statici pubblici), quindi è solo la presenza dell'attributo default_interface
che ne causa la generazione (denominata IStateTriggerBase).
[default_interface]
unsealed runtimeclass StateTriggerBase
{
protected void SetActive(Boolean IsActive);
};
Nota
Questo attributo non è supportato per i costruttori. Se non è possibile eseguire l'overload dei costruttori per arity, è possibile definire metodi factory di overload, ad esempio gli esempi CreateFromUri e CreateFromStream illustrati nell'ultimo frammento di codice in questa sezione.
L'attributo default_overload
rappresenta la classe DefaultOverloadAttribute , che indica che un metodo è il metodo di overload predefinito. Questa sezione illustra il motivo e le linee guida di utilizzo per l'attributo [default_overload]
.
È possibile eseguire liberamente l'overload Windows Runtime metodi per arity. In altri modi, è possibile definire più metodi con lo stesso nome, purché ognuno di essi accetta un numero diverso di argomenti. Ai fini dell'usabilità, è consigliabile aggiungere nuovi parametri alla fine e che il comportamento della funzione shorter-parameter-list sia equivalente alla chiamata della funzione longer-parameter-list con valori predefiniti naturali (specifici dello scenario) per i parametri mancanti.
runtimeclass Widget
{
void Start();
void Start(StartMode mode);
void Start(StartMode mode, Widget parent);
}
Nell'esempio precedente, è possibile chiamare Start con due parametri per specificare una modalità di avvio e un widget padre. Se si omette il widget padre, il valore predefinito è Null. Se si omette la modalità , viene avviata in una modalità predefinita specifica dello scenario.
In questo esempio successivo si usa DeviceInformation.CreateWatcher .
runtimeclass DeviceInformation
{
static DeviceWatcher CreateWatcher();
static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
static DeviceWatcher CreateWatcher(String aqsFilter);
static DeviceWatcher CreateWatcher(String aqsFilter,
IIterable<String> additionalProperties);
static DeviceWatcher CreateWatcher(String aqsFilter,
IIterable<String> additionalProperties,
DeviceInformationKind kind);
}
In questo esempio sono presenti cinque overload e la maggior parte di essi segue il modello consigliato per fare in modo che ogni nuovo overload sia un'estensione dell'overload precedente.
Se si decide di avere più overload di un metodo con lo stesso numero di parametri, viene visualizzato un errore del compilatore:
Gli overload a 1 parametro di DeviceInformation.CreateWatcher devono avere esattamente un metodo specificato come overload predefinito decorarlo con Windows. Foundation.Metadata.DefaultOverloadAttribute.
Il motivo è che alcuni linguaggi di programmazione sono tipi di dati dinamicamente. JavaScript e Python sono due esempi. Per questi linguaggi, la selezione dell'overload considera solo il numero di parametri e non i relativi tipi. Ciò significa che una chiamata JavaScript DeviceInformation.createWatcher(v);
a è ambigua: v deve essere forzato a deviceclass o a una stringa?
Per risolvere l'ambiguità, è necessario applicare l'attributo [default_overload]
a uno dei metodi . Ma come scegliere quale?
È consigliabile scegliere l'overload predefinito in modo che la funzionalità degli overload non predefiniti sia ancora disponibile con altri mezzi, in genere da uno degli altri overload.
Nell'esempio DeviceInformation.CreateWatcher la funzionalità dell'overload String può essere ottenuta chiamando l'overload String, IIterableString<> e passando un elenco vuoto di proprietà. D'altra parte, l'overload DeviceClass è l'unico modo per creare un DeviceWatcher filtrato in base a DeviceClass.
In questo modo il metodo DeviceClass è il chiaro vincitore. Si indica quindi che applicando l'attributo [default_overload]
a tale overload.
runtimeclass DeviceInformation
{
static DeviceWatcher CreateWatcher();
[default_overload]
static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
static DeviceWatcher CreateWatcher(String aqsFilter);
static DeviceWatcher CreateWatcher(String aqsFilter,
IIterable<String> additionalProperties);
static DeviceWatcher CreateWatcher(String aqsFilter,
IIterable<String> additionalProperties,
DeviceInformationKind kind);
}
Cosa succede se non c'è un vincitore perché tutti gli overload in conflitto forniscono funzionalità univoche non disponibili da altri overload?
runtimeclass Widget
{
static Widget Create(Uri source);
static Widget Create(IInputStream source);
}
L'ipotetico oggetto Widget può essere creato da un URI o da un flusso di input. Se si contrassegna uno come overload predefinito, i linguaggi di programmazione con tipi di dati dinamicamente perdono completamente l'accesso all'altro.
Per risolvere questo problema, assegnare alle due versioni nomi diversi in modo che non siano sottoposti a overload.
runtimeclass Widget
{
static Widget CreateFromUri(Uri source);
static Widget CreateFromStream(IInputStream source);
}
Entrambi i modelli di creazione sono ora disponibili per tutti i linguaggi.
Vedere anche Overload del metodo eproiezione basata su classi.
L'attributo interface_name
specifica il nome e l'IID dell'interfaccia che contiene i membri dell'istanza della classe . Per impostazione predefinita, il compilatore assegna nomi di interfaccia usando lo stesso algoritmo di numerazione univoco utilizzato per i metodi.
Nell'esempio seguente l'attributo interface_name
applicato alla classe runtime specifica il nome e l'IID dell'interfaccia che contiene tutti i membri di istanza della classe che non sono altrimenti assegnati a un'interfaccia. Quindi, LineHeight, LineStackingStrategy, Margin e TextAlignment sono membri dell'interfaccia IBlock .
Tuttavia, HorizontalTextAlignment è un membro dell'interfaccia IBlock2 , interface_name
a causa dell'attributo che include tale membro.
[interface_name("Windows.UI.Xaml.Documents.IBlock", 4bce0016-dd47-4350-8cb0-e171600ac896)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
...
Double LineHeight;
Windows.UI.Xaml.LineStackingStrategy LineStackingStrategy;
Windows.UI.Xaml.Thickness Margin;
Windows.UI.Xaml.TextAlignment TextAlignment;
[interface_name("Windows.UI.Xaml.Documents.IBlock2", 5ec7bdf3-1333-4a92-8318-6caedc12ef89)]
{
Windows.UI.Xaml.TextAlignment HorizontalTextAlignment;
}
...
}
È anche possibile usare l'attributo interface_name
per forzare la generazione di un'interfaccia. Nell'esempio seguente , StateTriggerBase non ha bisogno di IStateTriggerBase ed è solo la presenza dell'attributo interface_name
che ne determina la generazione.
[interface_name("Windows.UI.Xaml.IStateTriggerBase", 48b20698-af06-466c-8052-93666dde0e49)]
unsealed runtimeclass StateTriggerBase
{
protected void SetActive(Boolean IsActive);
};
Di conseguenza, quando la descrizione della classe non fa altrimenti riferimento a un'interfaccia, ma quando ne è necessaria una per implementare la classe, il compilatore MIDL 3.0 sintetizza e aggiunge le interfacce, se necessario.
Se si usa inutilmente default_interface
, MIDL 3.0 genera un'interfaccia vuota aggiuntiva e lo rende l'impostazione predefinita.
Ogni Windows Runtime ha un'interfaccia ABI (Application Binary Interface) equivalente. L'interfaccia ABI richiede che tutti i membri hanno nomi univoci. In MIDL 3.0 esistono due casi in cui i membri non hanno un nome o non hanno un nome univoco.
- costruttori e
- due o più metodi di overload.
In questi casi, il compilatore MIDL 3.0 sintetizza un nome di membro univoco, in base alle esigenze.
Per impostazione predefinita, il compilatore < assegna ai metodi del costruttore i nomi className>, <className2>, <className3> e così via per i metodi equivalenti nell'interfaccia ABI. In altre parole, viene aggiunto il suffisso numerico intero più piccolo inutilizzato (crescente da 2) per rendere univoco il nome del metodo del costruttore.
Analogamente, per i metodi di overload, per il primo metodo di una serie di overload (in ordine lessicale), il compilatore usa il nome del metodo originale per il metodo di interfaccia ABI equivalente. Gli overload successivi vengono resi univoci aggiungendo al nome originale il più piccolo suffisso numerico intero inutilizzato (in aumento da 2).
Ad esempio, il linguaggio IDL seguente dichiara tre overload di DoWork e due overload di un metodo diverso denominato DoWork3.
void DoWork(Int32 x);
void DoWork3(Int32 x);
void DoWork(Int32 x, Int32 y);
void DoWork(Int32 x, Int32 y, Int32 z);
void DoWork3(Int32 x, Int32 y);
Per impostazione predefinita (poiché il nome DoWork3 è già stato assunto), il compilatore assegna ai tre overload di DoWork i nomi
- Dowork
- DoWork2
- DoWork4.
DoWork3 non è un overload di DoWork . Per impostazione predefinita, il compilatore assegna ai due overload di DoWork3 i nomi
- DoWork3
- DoWork32.
In ordine vtable, quindi, le funzioni verranno visualizzate come
- Dowork
- DoWork3
- DoWork2
- DoWork4
- DoWork32
È possibile eseguire l'override dell'assegnazione del nome predefinita del compilatore usando l'attributo method_name
.
Nell'esempio seguente viene indicato al compilatore di usare il nome CreateInstance per il membro del costruttore predefinito Block.
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
...
[method_name("CreateInstance")] protected Block();
...
}
L'attributo static_name
specifica il nome e l'IID dell'interfaccia che contiene i membri statici della classe.
Nell'esempio seguente, static_name
l'attributo applicato alla classe runtime specifica il nome e l'IID dell'interfaccia che contiene tutti i membri statici della classe che non sono altrimenti assegnati a un'interfaccia. LineHeightProperty, LineStackingStrategyProperty, MarginProperty e TextAlignmentProperty sono membri dell'interfaccia IBlockStatics.
Tuttavia, HorizontalTextAlignmentProperty è un membro dell'interfaccia IBlockStatics2 , static_name
a causa dell'attributo che include tale membro.
[static_name("Windows.UI.Xaml.Documents.IBlockStatics", f86a8c34-8d18-4c53-aebd-91e610a5e010)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
...
static Windows.UI.Xaml.DependencyProperty LineHeightProperty{ get; };
static Windows.UI.Xaml.DependencyProperty LineStackingStrategyProperty{ get; };
static Windows.UI.Xaml.DependencyProperty MarginProperty{ get; };
static Windows.UI.Xaml.DependencyProperty TextAlignmentProperty{ get; };
[static_name("Windows.UI.Xaml.Documents.IBlockStatics2", af01a4d6-03e3-4cee-9b02-2bfc308b27a9)]
{
static Windows.UI.Xaml.DependencyProperty HorizontalTextAlignmentProperty{ get; };
}
...
}