Convenzioni di codifica per l'API dei metadati
In questo argomento vengono illustrate le convenzioni di codifica utilizzate dall'API dei metadati.
Gestione dei parametri di stringa
L'API dei metadati espone tutte le stringhe in formato Unicode. Il formato su disco per i nomi dei simboli in realtà è UTF-8, ma è nascosto dai client dell'API dei metadati. Ogni stringa restituita è costituita da una serie di tre parametri, i cui nomi veri e propri possono variare:
[in] ULONG cchString: dimensione in byte del buffer in cui deve essere restituita la stringa, incluso il carattere di terminazione null.
[out] LPCWSTR wzString: puntatore al buffer in cui viene restituita la stringa.
[out] ULONG *pchString: puntatore alla dimensione della stringa restituita, incluso il carattere di terminazione null. Se il buffer è troppo piccolo per archiviare l'intera stringa, la stringa restituita sarà troncata, verrà inviata un'indicazione di errore e il client potrà allocare di nuovo il buffer ed eventualmente ritentare.
Nomi dei simboli
Ai nomi dei simboli per i parametri di stringa si applicano le seguenti convenzioni:
Si presume che i parametri di stringa che rappresentano nomi di simboli abbiano sempre un carattere di terminazione null, pertanto non è necessario alcun parametro [in] di lunghezza (length). Non sono supportati caratteri null incorporati.
Se una stringa di parametro [in] è troppo estesa per essere salvata in modo permanente senza essere troncata, verrà restituito un errore.
Stringhe utente
Ai parametri di stringa forniti dall'utente si applicano le seguenti convenzioni:
Le stringhe utente possono avere caratteri null incorporati e non devono contenere un carattere di terminazione null.
Nel parametro cchString deve essere fornita una lunghezza. La dimensione del buffer deve corrispondere esattamente alla lunghezza della stringa che verrà archiviata.
Archiviazione dei valori predefiniti
Le costanti possono essere archiviate nei metadati come valori predefiniti per campi, parametri e proprietà. Per specificare una costante vengono utilizzati tre parametri, i cui nomi veri e propri possono variare:
[in] DWORD dwCPlusTypeFlag: valore dell'enumerazione CorElementType che specifica il tipo del valore predefinito.
[in] void const *pValue: puntatore al valore predefinito effettivo. Ad esempio, un puntatore al parametro DWORD di 4 byte contenente 0x0000002A archivierà un valore DWORD di 42 decimali nei metadati. Il tipo del valore predefinito, specificato nel parametro dwCPlusTypeFlag, può essere soltanto una primitiva o una stringa. Se il parametro dwCPlusTypeFlag è ELEMENT_TYPE_CLASS, il valore predefinito sarà null.
[in] ULONG cchValue: numero di caratteri Unicode nella sequenza di byte a cui punta il parametro pValue. È richiesto solo se il tipo, specificato in dwCPlusTypeFlag, è ELEMENT_TYPE_STRING. In tutti gli altri casi, la lunghezza viene derivata dal tipo.
I valori predefiniti non vengono inseriti automaticamente nel codice di inizializzazione o in aree dati inizializzate staticamente. Vengono semplicemente registrati nei metadati.
Per indicare che non si desidera specificare un valore predefinito, impostare tutti i bit di dwCPlusTypeFlag, ovvero impostare il valore su -1.
Puntatori null per i parametri restituiti
Poiché le API dei metadati eseguono un controllo minimo degli errori, prevedono la presenza di un puntatore diverso da null per i parametri restituiti nei seguenti casi:
Nei metodi Define è richiesto un puntatore diverso da null per il token restituito. Questi metodi creano l'elemento che si desidera venga definito e restituiscono un token per l'elemento. È possibile scegliere di eliminare il token se non è necessario.
I metodi Find restituiscono sempre il token per l'elemento, se viene trovato.
Nei metodi Get è possibile passare null nei parametri non più necessari.
Nei metodi Set generalmente non vi sono valori restituiti. Il token per l'elemento da aggiornare viene passato insieme ai valori da aggiornare e questi metodi eseguono l'aggiornamento.
Valori dei parametri da ignorare
Numerosi metodi delle API dei metadati consentono di modificare le proprietà di un elemento definito in precedenza. Nel seguente esempio viene utilizzato il metodo IMetaDataEmit::SetFieldProps per modificare le proprietà di un campo precedentemente fornite in una chiamata al metodo IMetaDataEmit::DefineField:
HRESULT SetFieldProps(mdFieldDef fd, DWORD dwFieldFlags,
DWORD dwDefType, void const *pValue, ULONG cchValue)
In alcune circostanze può accadere di dover modificare dwFieldFlags, ma non pValue o viceversa. In questo caso, per evitare un errore è necessario passare il valore di un parametro, anche se non si desidera modificare tale valore. Tuttavia, per evitare la modifica è possibile passare un determinato valore che indichi che l'argomento deve essere ignorato. Nelle API dei metadati vengono utilizzate le seguenti convenzioni per indicare che un argomento di un metodo deve essere ignorato:
Se il parametro è un tipo puntatore, passare un puntatore null.
Se il parametro è un tipo valore, in genere una maschera di bit dei flag, passare un valore con tutti i bit impostati (-1).
Restituzione di errori
Quasi tutti metodi delle interfacce IMetaDataDispenserEx, IMetaDataEmite IMetaDataImport restituiscono un valore HRESULT per indicare il risultato. Il valore sarà S_OK se l'operazione è stata eseguita correttamente. Se la chiamata ha esito negativo, verrà restituito un altro valore per descrivere la ragione per cui l'operazione non è riuscita.
Come regola generale per tutte le API dei metadati, se il chiamante fornisce un buffer di stringa troppo piccolo per contenere i risultati, le API copiano il maggior numero possibile di caratteri, ma restituiscono CLDB_S_TRUNCATION come valore HRESULT invece di S_OK.
I chiamanti delle interfacce IMetadata sono compilatori o strumenti. È importante che tali chiamanti verifichino sempre lo stato restituito da ogni chiamata in modo da rilevare gli errori. In questi casi, le condizioni di errore indicano un problema del chiamante diretto, ad esempio un compilatore, piuttosto che dell'utente, ad esempio un programma applicativo.
Gestione della memoria
Mediante l'impostazione COM generica predefinita il chiamante libera la memoria allocata dal chiamato. Tuttavia, i metodi dei metadati operano in modo diverso.
Molti metodi dei metadati restituiscono [out] puntatori ai blocchi di memoria. Tale memoria è parte dell'heap dei metadati del modulo ed è di proprietà di Common Language Runtime. Pertanto, viene fornito un puntatore direttamente nell'archiviazione in memoria dei metadati di Common Language Runtime e l'applicazione non dovrà liberare la memoria.
Supporto dei generics
In .NET Framework versione 2.0 le API dei metadati sono state estese in modo significativo per supportare i generics, a volte indicati anche come "polimorfismo dei parametri". I generics sono simili ai modelli di C++. Di seguito è riportato un esempio di definizione di una classe generica in C#:
public class Dictionary<Key, Val> { . . . }
In questo caso, la classe Dictionary presenta due parametri generici, denominati Key e Val. Quando viene creata un'istanza della classe, l'utente seleziona i tipi per i parametri generici, come nel seguente esempio:
Dictionary<string, int> NameToPhone = new Dictionary<string, int>();
Dictionary<int, string> PhoneToName = new Dictionary<int, string>();