Convenciones de codificación para API de metadatos
En este tema se explican las convenciones de codificación utilizadas por la API de metadatos.
Controlar parámetros de cadena
La API de metadatos expone todas las cadenas en formato Unicode. (El formato de los nombres de símbolo en el disco es realmente UTF-8, pero este dato se oculta a los clientes de la API de metadatos). Cada cadena devuelta es un valor triple de tres parámetros (los nombres reales de los parámetros varían):
[in] ULONG cchString: Tamaño, en bytes, del búfer en el que se devolverá la cadena, incluido el carácter null final.
[out] LPCWSTR wzString: Puntero al búfer en el que se devuelve la cadena.
[out] ULONG *pchString: Puntero al tamaño de la cadena devuelta (incluido el carácter null final). Si el búfer es demasiado pequeño para almacenar la cadena completa, la cadena devuelta se trunca, se devuelve una indicación del error y el cliente puede reasignar el búfer y volver a intentarlo.
Nombres de símbolos
Las siguientes convenciones se aplican a los nombres de símbolo para los parámetros de cadena:
Siempre se asume que los parámetros de cadena que son nombres de símbolo finalizan con un carácter nulo y no se necesita un parámetro de longitud [in]. No se admiten caracteres null incrustados.
Si una cadena de parámetro [in] es demasiado grande y no se puede conservar sin ser truncada, se devolverá un error.
Cadenas de usuario
Las convenciones siguientes se aplican a los parámetros de cadena proporcionados por el usuario:
Las cadenas de usuario pueden tener caracteres null incrustados y no deben tener un carácter null final.
Debe proporcionarse una longitud en el parámetro cchString. El tamaño del búfer debe ser la longitud exacta de la cadena que se almacenará.
Almacenar valores predeterminados
Se pueden almacenar constantes en los metadatos como valores predeterminados para campos, parámetros y propiedades. Para especificar una constante se utilizan tres parámetros (los nombres reales de los parámetros varían):
[in] DWORD dwCPlusTypeFlag: Valor de la enumeración CorElementType que especifica el tipo de valor predeterminado.
[in] void const *pValue: Puntero al valor real predeterminado. Por ejemplo, un puntero al valor DWORD de 4 bytes con 0x0000002A almacenará un valor DWORD de 42 decimales en los metadatos. El tipo (especificado en dwCPlusTypeFlag) del valor predeterminado se limita a un tipo primitivo o un tipo de cadena. Si dwCPlusTypeFlag es ELEMENT_TYPE_CLASS, el valor predeterminado será null.
[in] ULONG cchValue: Número de caracteres Unicode en la secuencia de bytes a los que señala pValue. Es necesario sólo si el tipo, especificado en dwCPlusTypeFlag, es ELEMENT_TYPE_STRING. En todos los demás casos, la longitud se deduce del tipo.
Los valores predeterminados no se insertan de forma automática en el código de inicialización ni en áreas de datos inicializadas estáticamente. Simplemente se registran en los metadatos.
Para indicar que no se desea especificar un valor predeterminado, establezca todos los bits de dwCPlusTypeFlag (es decir, establezca el valor en -1).
Punteros nulos para parámetros devueltos
Dado que las API de metadatos realizan una comprobación de errores mínima, esperan un puntero no nulo para los parámetros devueltos en las circunstancias siguientes:
En los métodos Define, se requiere un puntero no nulo para el símbolo (token) devuelto. Estos métodos crean el elemento que se desea definir y devuelven un símbolo (token) para el elemento. Puede decidir descartar el token si no lo necesita.
Los métodos Find siempre devuelven el token del elemento si lo encuentran correctamente.
En los métodos Get, se puede pasar null en los parámetros que no es necesario recuperar.
En los métodos Set, por lo general no se devuelve ningún valor. Se pasa el token del elemento que se va a actualizar junto con los valores que deben actualizarse, y estos métodos realizan la actualización.
Valores de parámetro que se omiten
Varios métodos de la API de metadatos permiten cambiar las propiedades de un elemento definido anteriormente. En el ejemplo siguiente se utiliza el método IMetaDataEmit::SetFieldProps para cambiar las propiedades de un campo, que se proporcionaron previamente en una llamada a IMetaDataEmit::DefineField:
HRESULT SetFieldProps(mdFieldDef fd, DWORD dwFieldFlags,
DWORD dwDefType, void const *pValue, ULONG cchValue)
En ocasiones, puede que desee cambiar dwFieldFlags, pero no pValue (o viceversa). En ese caso, debe pasar un valor de parámetro para evitar que se produzca un error, aun cuando no desee cambiar el valor. Sin embargo, puede pasar un valor concreto que indique que el argumento debe omitirse en caso de que no desee cambiar su valor. Las API de metadatos utilizan las convenciones siguientes para indicar que se debería omitir un argumento de método:
Si el parámetro es un tipo de puntero, pase un puntero nulo.
Si el parámetro es un tipo de valor (normalmente, una máscara de bits de marcadores), pase un valor donde todos los bits estén establecidos (-1).
Devolución de errores
Casi todos métodos de las interfaces IMetaDataDispenserEx, IMetaDataEmit e IMetaDataImport devuelven un valor HRESULT para indicar su resultado. El valor será S_OK si la operación fue correcta. Si la llamada fue incorrecta, devuelve otro valor para describir el motivo del error de la operación.
El comportamiento general de todas las API de metadatos es que, si el llamador proporciona un búfer de cadena demasiado pequeño para contener los resultados, las API copian todos los caracteres que caben, pero devuelven el valor HRESULT de CLDB_S_TRUNCATION, en lugar de S_OK.
Los llamadores de las interfaces IMetadata son compiladores o herramientas. Es importante que estos llamadores comprueben siempre el estado de retorno de cada llamada para detectar errores. En estos casos, las condiciones de error reflejan un problema del llamador directo (como un compilador) en lugar del usuario (como un programa de aplicación).
Administración de la memoria
El comportamiento predeterminado COM genérico es que el llamador libera la memoria que el destinatario asigna. Sin embargo, los métodos de los metadatos funcionan de manera diferente.
Muchos métodos de los metadatos devuelven [out] punteros a bloques de memoria. Esa memoria es parte del montón de los metadatos del módulo y su propietarios es Common Language Runtime (CLR). Por consiguiente, se proporciona un puntero directamente al almacenamiento en memoria de los metadatos de CLR y no se pide a la aplicación que libere la memoria.
Compatibilidad para genéricos
En .NET Framework 2.0, las API de metadatos se han ampliado significativamente para admitir genéricos, lo que se conoce también como "polimorfismo paramétrico". Los genéricos son similares a las plantillas de C++. Un ejemplo de definición de una clase genérica en C# podría ser el siguiente:
public class Dictionary<Key, Val> { . . . }
En este caso, la clase Dictionary se parametriza con dos parámetros genéricos denominados Key y Val. Cuando se crean instancias de la clase, el usuario selecciona los tipos para los parámetros genéricos, como en el ejemplo siguiente:
Dictionary<string, int> NameToPhone = new Dictionary<string, int>();
Dictionary<int, string> PhoneToName = new Dictionary<int, string>();