Compartir a través de


Introducción al lenguaje de definición de interfaz de Microsoft 3.0

Microsoft Interface Definition Language (MIDL) 3.0 es una sintaxis simplificada y moderna para definir tipos de Windows Runtime dentro de archivos del lenguaje de definición de interfaz (IDL) (.idl archivos). Esta nueva sintaxis se sentirá familiar con cualquier persona experimentada con C, C++, C# o Java. MIDL 3.0 es una manera especialmente cómoda de definir clases en tiempo de ejecución de C++/WinRT, siendo drásticamente más concisa que las versiones anteriores de IDL (reduciendo los diseños por dos tercios de longitud y usando valores predeterminados razonables para reducir la necesidad de decorar con atributos).

Este es el aspecto de MIDL 3.0; En este ejemplo se muestra la mayoría de los elementos de sintaxis del lenguaje que probablemente usará.

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

Tenga en cuenta que la sintaxis de MIDL 3.0 está diseñada específicamente y exclusivamente para definir tipos de. Usará un lenguaje de programación diferente para implementar esos tipos. Para usar MIDL 3.0, necesitará Windows SDK versión 10.0.17134.0 (Windows 10, versión 1803) (midl.exe versión 8.01.0622 o posterior, que se usa con el modificador /winrt).

Nota

Consulta también la referencia consolidada de Windows Runtime (El sistema de tipos de Windows Runtimey archivos de metadatos de Windows).

MIDL 1.0, 2.0 y 3.0

Interface Definition Language (IDL) comenzó con el sistema Distributed Computing Environment/Remote Procedure Calls (DCE/RPC). El MIDL 1.0 original es DCE/RPC IDL con mejoras para definir interfaces COM y coclases.

Una sintaxis MIDL 2.0 actualizada (también conocida como MIDLRT) se desarrolló en Microsoft para declarar las API de Windows Runtime para la plataforma Windows. Si busca en la carpeta windows SDK %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt, verá ejemplos de archivos .idl escritos con la sintaxis MIDL 2.0. Estas son API integradas de Windows Runtime, declaradas en su forma de interfaz binaria de aplicación (ABI). Estos archivos existen principalmente para herramientas que se van a usar: no creará ni consumirá estas API en este formato (a menos que escriba código de bajo nivel).

Consulte también Transición a MIDL 3.0 desde midlRT clásico.

MIDL 3.0 es una sintaxis mucho más sencilla y moderna, cuyo propósito es declarar las API de Windows Runtime. Y puede usarlo en los proyectos, especialmente para definir clases en tiempo de ejecución de C++/WinRT. Los encabezados, para usarlos desde C++/WinRT, para las API integradas de Windows Runtime forman parte del SDK, dentro de la carpeta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Casos de uso para MIDL 3.0

En general, todas las API de Windows Runtime están diseñadas para estar disponibles para todas las proyecciones de lenguaje de Windows Runtime. Esto se hace, en parte, eligiendo pasar exclusivamente tipos de Windows Runtime a las API de Windows Runtime y desde estas. Aunque es una decisión de diseño válida pasar una interfaz COM sin procesar a y desde una API de Windows Runtime, esto limita los consumidores de esa API de Windows Runtime concreta a aplicaciones de C++. La técnica se puede ver en escenarios de interoperación, por ejemplo, al interoperar entre Direct3D y XAML. Dado que Direct3D está en la imagen, el escenario se limita necesariamente a las aplicaciones de C++. Por lo tanto, una API que requiere una interfaz COM no impone ninguna limitación adicional sobre y por encima de lo que es inherente. Por ejemplo, una aplicación de C++ puede obtener un idXGISwapChain puntero de interfaz y, a continuación, pasarlo al método ISwapChainPanelNative::SetSwapChain. Por ejemplo, una aplicación de C# no podría obtener una IDXGISwapChain comenzar, por lo que no podría usar ese método por ese motivo. Estas excepciones relacionadas con la interoperabilidad residen en encabezados de interoperabilidad, como windows.ui.xaml.media.dxinterop.h.

Si son características o funcionalidades de un componente COM que quieres exponer a proyecciones de lenguaje de Windows Runtime más allá de C++, puedes crear un componente de C++ Windows Runtime (CMR) que crea y usa directamente el componente COM (como DirectX, por ejemplo), y expone una replicación de algunos subconjuntos de sus características y funcionalidades en forma de una superficie de API de Windows Runtime que toma y devuelve Windows. Solo tipos en tiempo de ejecución. Después, podrías consumir ese CMR desde una aplicación escrita en cualquier proyección de lenguaje de Windows Runtime.

Estructura de definición y llamada a midl.exe desde la línea de comandos

Los conceptos clave de la organización en una definición MIDL 3.0 son espacios de nombres, tipos y miembros. Un archivo de origen MIDL 3.0 (un archivo .idl) contiene al menos un espacio de nombres, dentro de los cuales son tipos o espacios de nombres subordinados. Cada tipo contiene cero o más miembros.

  • Las clases, interfaces, estructuras y enumeraciones son tipos.
  • Los métodos, propiedades, eventos y campos son ejemplos de miembros.

Al compilar un archivo de origen MIDL 3.0, el compilador (midl.exe) emite un archivo de metadatos de Windows Runtime (normalmente un archivo de .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);
    }
}

Dado que el espacio de nombres de un tipo de Windows Runtime forma parte del nombre de tipo, en el ejemplo anterior se define una clase en tiempo de ejecución denominada Bookstore.BookSku. No hay ninguna forma independiente del idioma de expresar BookSku sin expresar también el espacio de nombres.

Esta clase implementa la interfaz Windows.UI.Xaml.Data.INotifyPropertyChanged. Y la clase contiene varios miembros: dos constructores, una propiedad de lectura y escritura (Price), algunas propiedades de solo lectura (AuthorName a través de Title) y dos métodos, denominados Equals y ApplyDiscount. Tenga en cuenta el uso del tipo Single en lugar de float. Y que string tiene una mayúscula "S".

Propina

Visual Studio proporciona la mejor experiencia para compilar MIDL 3.0, mediante la extensión de Visual Studio de C++/WinRT (VSIX). Consulte compatibilidad de Visual Studio con C++/WinRT y vsIX.

Pero también puede compilar MIDL 3.0 desde la línea de comandos. Si el código fuente de este ejemplo se almacena en un archivo denominado Bookstore.idl, puede emitir el comando siguiente. Si es necesario para su caso, puede actualizar el número de versión del SDK usado en el comando (que es 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

La herramienta midl.exe compila el ejemplo y genera un archivo de metadatos denominado Bookstore.winmd (de forma predeterminada, se usa el nombre del archivo .idl).

Propina

Si usa más de un archivo IDL (para obtener consejos sobre eso, consulte clases en tiempo de ejecución de Factoring en archivos Midl (.idl)), combine todos los archivos de .winmd resultantes en un solo archivo con el mismo nombre que el espacio de nombres raíz. Ese archivo final .winmd será el que harán referencia los consumidores de las API.

En este caso, bookSku es la única clase en tiempo de ejecución del espacio de nombres Bookstore, por lo que se guardó un paso y acabamos de nombrar el archivo .idl para el espacio de nombres.

Por cierto, puede usar el comando where para averiguar dónde está instalado midl.exe.

where midl

Si desea usar los tipos definidos en un archivo .idl de un archivo de .idl diferente, use la directiva import. Para obtener más información y un ejemplo de código, consulta controles XAML; enlazar a una propiedad de C++/WinRT. Por supuesto, si usa un componente integrado o de terceros, no tendrá acceso al archivo .idl. Por ejemplo, puede que quieras consumir el Win2D API de Windows Runtime para la representación de gráficos 2D en modo inmediato. El comando anterior usó el modificador /reference para hacer referencia a un archivo de metadatos de Windows Runtime (.winmd). En este ejemplo siguiente, usaremos ese modificador de nuevo, imaginando el escenario en el que tenemos Bookstore.winmd, pero no Bookstore.idl.

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

Si el código fuente del ejemplo anterior se almacena en un archivo denominado MVVMApp.idl, puede emitir el comando siguiente para hacer referencia 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

Espacios de nombres

Se requiere un espacio de nombres. Prefijos el nombre de todos los tipos definidos en el ámbito del bloque de espacio de nombres con el nombre del espacio de nombres. Un espacio de nombres también puede contener declaraciones de espacio de nombres subordinados. El nombre de los tipos definidos en un ámbito de espacio de nombres subordinado tiene un prefijo de todos los nombres de espacio de nombres que contienen.

Los ejemplos siguientes son dos maneras de declarar la misma clase Windows.Foundation.Uri (como puede ver, los puntos separan los niveles de los espacios de nombres anidados).

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

Este es otro ejemplo que muestra que es legal declarar espacios de nombres y sus tipos de forma anidada.

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

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

Pero es más habitual cerrar el espacio de nombres anterior y abrir uno nuevo, como este.

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

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

Tipos

Hay dos tipos de tipos de datos en MIDL 3.0: tipos de valor y tipos de referencia. Una variable de un tipo de valor contiene directamente sus datos. Una variable de un tipo de referencia almacena una referencia a sus datos (esta variable también se conoce como un objeto ).

Es posible que dos variables de tipo de referencia hagan referencia al mismo objeto. Por lo tanto, una operación en una variable afecta al objeto al que hace referencia la otra variable. Con los tipos de valor, las variables tienen su propia copia de los datos y no es posible que una operación en una afecte a la otra.

Los tipos de valor de MIDL 3.0 se dividen aún más en tipos simples, tipos de enumeración, tipos de estructura y tipos que aceptan valores NULL.

Los tipos de referencia de MIDL 3.0 se dividen aún más en tipos de clase, tipos de interfaz y tipos delegados.

Esta es una introducción al sistema de tipos MIDL 3.0. A diferencia de las versiones anteriores de MIDL, no puede usar alias para estos tipos.

Categoría Descripción
Tipos de valor Tipos simples Entero firmado: Int16, Int32, Int64
Entero sin signo: UInt8, UInt16, UInt32, UInt64
Caracteres Unicode: char (representa un UTF-16LE; una unidad de código Unicode de 16 bits)
Cadenas Unicode: string de
Punto flotante IEEE: single, double
Boolean: booleano
UUID de 128 bits: guid
Tipos de enumeración Tipos definidos por el usuario del formulario enumeración E {...}
Tipos de estructura Tipos definidos por el usuario del formulario estructura S {...}
Tipos que aceptan valores NULL Extensiones de todos los demás tipos de valor con un valor de null
Tipos de referencia Tipos de clase Clase base ultimate de todos los demás tipos: Object
Tipos definidos por el usuario del formulario runtimeclass C {...}
Tipos de interfaz Tipos definidos por el usuario del formulario interfaz I {...}
Tipos delegados Tipos definidos por el usuario del formulario delegado <returnType> D(...)

Los siete tipos enteros proporcionan compatibilidad con datos sin signo de 8 bits; y valores de 16 bits, 32 bits y 64 bits en formato con signo o sin signo.

Los dos tipos de punto flotante, single y double, representan los datos que usan los formatos de precisión simple de 32 bits y de doble precisión IEEE 754 de 64 bits, respectivamente.

El tipo de booleano MIDL 3.0 representa valores booleanos; o .

Los caracteres y cadenas de MIDL 3.0 contienen caracteres Unicode. El tipo char de representa una unidad de código UTF-16LE; y el tipo string de representa una secuencia de unidades de código UTF-16LE.

En la tabla siguiente se resumen los tipos numéricos de MIDL 3.0.

Categoría Bits Tipo Rango y precisión
Entero firmado 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
Entero sin signo 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
Coma flotante 32 único 1,5 × 10-45 a 3,4 × 1038, precisión de 7 dígitos
64 doble 5,0 × 10-324 a 1,7 × 10308, precisión de 15 dígitos

Los archivos de origen MIDL 3.0 usan definiciones de tipos para crear nuevos tipos. Una definición de tipo especifica el nombre y los miembros del nuevo tipo. Estas categorías de tipos MIDL 3.0 son definibles por el usuario.

  • tipos de atributo,
  • tipos de estructura,
  • tipos de interfaz,
  • tipos runtimeclass,
  • tipos de delegado y
  • tipos de enumeración.

Un atributo tipo define un atributo de Windows Runtime que se puede aplicar a otras definiciones de tipo. Un atributo proporciona metadatos sobre el tipo al que se aplica el atributo.

Un tipo estructura define una estructura de Windows Runtime que contiene miembros de datos (campos). Las estructuras son tipos de valor y no requieren asignación de montón. Un miembro de datos de un tipo de estructura debe ser un tipo de valor o un tipo que acepta valores NULL. Los tipos de estructura no admiten la herencia.

Un interfaz tipo define una interfaz de Windows Runtime, que es un conjunto con nombre de miembros de función. Una interfaz puede especificar que una implementación de la interfaz también debe implementar una o varias interfaces adicionales (necesarias) especificadas. Cada tipo de interfaz deriva directamente de la interfaz IInspectable de Windows Runtime.

Un tipo runtimeclass define una clase de Windows Runtime (clase en tiempo de ejecución). Una clase en tiempo de ejecución contiene miembros que pueden ser propiedades, métodos y eventos.

Un delegado tipo define un delegado de Windows Runtime, que representa una referencia a un método con una lista de parámetros determinada y un tipo de valor devuelto. Los delegados permiten tratar un método como una entidad que se puede pasar como parámetro. Un delegado es similar al concepto de puntero de función que se encuentra en otros lenguajes. A diferencia de los punteros de función, los delegados están orientados a objetos y son seguros para tipos.

Un tipo enumeración es un tipo distinto con constantes con nombre. Cada tipo de enumeración tiene un tipo subyacente implícito; int32 o UInt32. El conjunto de valores de un tipo de enumeración es el mismo que el conjunto de valores del tipo subyacente.

MIDL 3.0 admite tres categorías de tipos adicionales.

  • tipos de matriz unidimensional,
  • Tipos de valor que aceptan valores NULL y
  • tipo de objeto .

No es necesario declarar una matriz unidimensional para poder usarla. En su lugar, los tipos de matriz se construyen siguiendo un nombre de tipo con corchetes. Por ejemplo, Int32[] es una matriz unidimensional de Int32.

Del mismo modo, tipos de valor que aceptan valores NULL tampoco es necesario definir antes de poder usarlos. Para cada tipo de valor que no acepta valores NULL T (excepto String), hay un tipo que acepta valores NULL correspondiente Windows.Foundation.IReference<T>, que puede contener el valor adicional null. Por ejemplo, Windows.Foundation.IReference<Int32> es un tipo que puede contener cualquier entero de 32 bits o el valor null. Consulte también IReference<T>.

Por último, MIDL 3.0 admite el tipo Object de , que se asigna a la interfaz de IInspectable de Windows Runtime . Los tipos de referencia de interfaz y runtimeclass derivan conceptualmente del tipo Object de ; delegado no.

Expresiones en un valor enumerado

Con MIDL 3.0, solo puede usar una expresión en la definición del valor de las constantes con nombre de un tipo enumerado; es decir, en un inicializador de enumeración.

Una expresión se construye a partir de operandos y operadores de . Los operadores de una expresión indican qué operaciones se aplicarán a los operandos. Entre los ejemplos de operadores se incluyen +, -, *, /y new. Algunos ejemplos de operandos son literales, campos, variables locales y expresiones.

Cuando una expresión contiene varios operadores, la prioridad de los operadores controla el orden en el que se evalúan los operadores individuales. Por ejemplo, la expresión x + y * z se evalúa como x + (y * z) porque el operador * tiene mayor prioridad que el operador + . Las operaciones lógicas tienen una prioridad menor que las operaciones bit a bit.

En la tabla siguiente se resumen los operadores de MIDL 3.0, que enumeran las categorías de operador en orden de prioridad de mayor a menor. Los operadores de la misma categoría tienen la misma prioridad.

Categoría Expresión Descripción
Primario x++ Incremento posterior
x-- Poscremento
Unario +x Identidad
-x Negación
!x Negación lógica
~x Negación bit a bit
++x Incremento previo
--x Decremento previo
Multiplicativo x * y Multiplicación
x/y División
x % y Resto
Aditivo x + y Suma, concatenación de cadenas, combinación de delegados
x : y Resta, eliminación de delegados
Turno x << y Mayús hacia la izquierda
x >> y Desplazamiento a la derecha
AND bit a bit x & y AND bit a bit entero
XOR bit a bit x ^ y XOR bit a bit entero
OR bit a bit x | y OR bit a bit entero
AND lógico x && y AND lógico booleano
OR lógico x || y OR lógico booleano

Clases

Clases (o clases en tiempo de ejecución) son los tipos más fundamentales de MIDL 3.0. Una clase es una definición de una agregación de métodos, propiedades y eventos en una sola unidad. Las clases admiten de herencia y polimorfismo: mecanismos en los que clases derivadas pueden ampliar y especializar clases base.

Defina un nuevo tipo de clase mediante una definición de clase. Una definición de clase comienza con un encabezado que especifica la palabra clave runtimeclass, el nombre de la clase, la clase base (si se especifica) y las interfaces implementadas por la clase . El encabezado va seguido del cuerpo de la clase, que consta de una lista de declaraciones de miembro escritas entre los delimitadores { y }.

Esta es una definición de una clase simple denominada Area.

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

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

Esto define una nueva clase de Windows Runtime denominada Area, que tiene un constructor que toma dos parámetros Int32, dos Int32 propiedades de lectura y escritura denominadas Height y Width, y una propiedad estática de solo lectura denominada NumberOfAreas.

De forma predeterminada, una clase runtimeclass está sellada y no se permite la derivación de ella. Consulte clases base.

Para enlazar XAML a un modelo de vista, la clase en tiempo de ejecución del modelo de vista debe definirse en MIDL. Consulta controles XAML; enlazar a una propiedad de C++/WinRT para obtener más detalles.

Puede declarar que una clase no admite ninguna instancia (y, en consecuencia, debe contener solo miembros estáticos) mediante el prefijo de la definición de clase en tiempo de ejecución con la palabra clave static. Al agregar un miembro no estático a la clase, se produce un error de compilación.

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

Una clase estática es diferente de una clase vacía. Consulte también clases vacías.

Puede indicar que una definición de clase está incompleta mediante el prefijo de la definición de clase en tiempo de ejecución con la palabra clave partial. Todas las definiciones de clase parcial encontradas por el compilador se combinan en una sola clase en tiempo de ejecución. Esta característica es principalmente para escenarios de creación xaml, donde algunas de las clases parciales se generan de forma automática.

Modificador Significado
estático La clase no tiene ninguna instancia. Por lo tanto, solo se permiten miembros estáticos.
parcial La definición de clase está incompleta.

Consulte composición y activación para los modificadores avanzados.

Modificadores de acceso a miembros

Como MIDL 3.0 es un lenguaje de definición para describir la superficie pública de los tipos de Windows Runtime, no es necesario que la sintaxis explícita declare la accesibilidad pública de un miembro. Todos los miembros son implícitamente públicos. Por eso MIDL 3.0 no requiere ni permite la palabra clave (con redundancia eficaz) public.

Clases base

Una definición de clase puede especificar una clase base siguiendo los parámetros de tipo y nombre de clase con dos puntos y el nombre de la clase base. La omisión de una especificación de clase base es la misma que la derivación del tipo Object (es decir, de IInspectable).

Nota

Las clases de modelo de vista (de hecho, cualquier clase en tiempo de ejecución que defina en la aplicación) no deben derivarse de una clase base.

Cualquier clase en tiempo de ejecución que defina en la aplicación que deriva de una clase base se conoce como una clase compuesta. Y hay restricciones en torno a las clases que se pueden componer. Para que una aplicación supere las pruebas Kit de certificación de aplicaciones de Windows que usa Visual Studio y Microsoft Store para validar los envíos (y, por tanto, para que la aplicación se ingera correctamente en Microsoft Store), una clase composable debe derivar en última instancia de una clase base de Windows. Lo que significa que la clase en la raíz de la jerarquía de herencia debe ser un tipo que se origine en un espacio de nombres Windows.*.

Consulta controles XAML; enlazar a una propiedad de C++/WinRT para obtener más detalles.

En el ejemplo siguiente, la clase base de Volume es Areay la clase base de Area es 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

Aquí, área y volumen se definen en el mismo archivo de origen. Para obtener una explicación de las ventajas y desventajas, consulte Clases en tiempo de ejecución de Factoring en archivos Midl (.idl).

Una clase hereda los miembros de su clase base. La herencia significa que una clase contiene implícitamente todos los miembros de su clase base, excepto los constructores de la clase base. Una clase derivada puede agregar nuevos miembros a los que hereda, pero no puede quitar la definición de un miembro heredado.

En el ejemplo anterior, Volume hereda las propiedades Height y Width de Area. Por lo tanto, cada instancia de Volume contiene tres propiedades: Height, Widthy Depth.

En general, las reglas de resolución de tipos requieren que un nombre de tipo esté completo cuando se hace referencia a este. Una excepción es cuando el tipo se ha definido en el mismo espacio de nombres que el tipo actual. El ejemplo anterior funciona como se escribe si Área y Volumen están en el mismo espacio de nombres.

Interfaces implementadas

Una definición de clase también puede especificar una lista de interfaces que implementa la clase. Las interfaces se especifican como una lista separada por comas de interfaces siguiendo la clase base (opcional).

En el ejemplo siguiente, la clase Area implementa la interfaz IStringable; y la clase Volume implementa IStringable y la interfaz hipotética 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;
}

En MIDL, no declara los miembros de la interfaz en la clase . Por supuesto, debe declararlos y definirlos en la implementación real.

Miembros

Los miembros de una clase son miembros estáticos o miembros de instancia. Un miembro estático pertenece a una clase . Un miembro de instancia pertenece a un objeto (es decir, una instancia de una clase).

En esta tabla se muestran los tipos de miembro que una clase puede contener.

Tipo de miembro Descripción
Constructores Acciones necesarias para inicializar una instancia de la clase o para inicializar la propia clase
Propiedades Acciones asociadas a la lectura y escritura de propiedades con nombre de una instancia de la clase o de la propia clase
Métodos Cálculos y acciones que puede realizar una instancia de la clase o por la propia clase
Eventos Notificaciones que puede generar una instancia de la clase

Constructores

MIDL 3.0 admite la declaración de constructores de instancia. Un constructor de instancia de es un método que implementa las acciones necesarias para inicializar una instancia de una clase. Es posible que los constructores no sean estáticos.

Un constructor se declara como un método de instancia (pero sin tipo de valor devuelto) y con el mismo nombre que la clase contenedora.

Los constructores de instancia se pueden sobrecargar. Por ejemplo, la clase test de siguiente declara tres constructores de instancia; uno sin parámetros (el constructor de predeterminado ), uno que toma un parámetro Int32 y otro que toma dos parámetros Double (constructores parametrizados).

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

Para obtener más información sobre la sintaxis de las listas de parámetros, consulte Métodos a continuación.

Las propiedades, los métodos y los eventos de instancia se heredan. Los constructores de instancia no se heredan (con una excepción) y una clase no tiene constructores de instancia distintos de los declarados realmente en la clase . Si no se proporciona ningún constructor de instancia para una clase, no se puede crear una instancia directa de la clase. Para esta clase, normalmente tendría un método de fábrica en otro lugar que devuelva una instancia de la clase .

La excepción es clases sin secar. Una clase sin secar puede tener uno o varios constructores protegidos.

Propiedades

Propiedades son conceptualmente similares a los campos (por ejemplo, campos de C# o los campos de una estructura MIDL 3.0). Las propiedades y los campos son miembros con un nombre y un tipo asociado. Sin embargo, a diferencia de los campos, las propiedades no denotan ubicaciones de almacenamiento. En su lugar, las propiedades tienen descriptores de acceso que especifican la función que se va a ejecutar al leer o escribir una propiedad.

Una propiedad se declara como un campo de estructura, salvo que la declaración termina con una palabra clave get o una palabra clave de set escrita entre los delimitadores { y }, y finalizando en un punto y coma.

Una propiedad que tiene una palabra clave get y una palabra clave set es una propiedad de lectura y escritura. Una propiedad que solo tiene una palabra clave get es una propiedad de solo lectura . Windows Runtime no admite propiedades de solo escritura.

Por ejemplo, la clase Area, vista anteriormente, contiene dos propiedades de lectura y escritura denominadas Height y Width.

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

La declaración de Width omite las llaves y las palabras clave get y set. La omisión implica que la propiedad es de lectura y escritura, y es semánticamente idéntica a proporcionar las palabras clave get y set en ese orden,get, seguida de set.

Además, solo puede especificar la palabra clave get para indicar que la propiedad es de solo lectura.

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

Windows Runtime no admite propiedades de solo escritura. Pero solo puede especificar la palabra clave set para revisar una propiedad de solo lectura existente en una propiedad de lectura y escritura. Tome esta versión de Área como ejemplo.

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

Si quieres que la propiedad SurfaceColor lectura y escritura, y no necesites mantener la compatibilidad binaria con definiciones anteriores de Área (por ejemplo, la clase Area es un tipo en una aplicación que vuelves a compilar cada vez), simplemente puedes agregar la palabra clave set a la declaración SurfaceColor existente como esta.

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

Por otro lado, necesita estabilidad binaria (por ejemplo, la clase Area de es un componente de una biblioteca que se envía a los clientes), no puede agregar la palabra clave a la declaración de propiedad existente. Al hacerlo, cambia la interfaz binaria a la clase .

En ese caso, agregue la propiedad set palabra clave a una definición adicional de la propiedad al final de la clase como esta.

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

El compilador genera un error para una propiedad de solo escritura. Pero eso no es lo que se hace aquí. Debido a la declaración anterior de la propiedad como de solo lectura, la adición de la palabra clave set no declara una propiedad de solo escritura, sino una propiedad de lectura y escritura.

La implementación de Windows Runtime de una propiedad es uno o dos métodos de descriptor de acceso en una interfaz. El orden de las palabras clave get y set en la declaración de propiedad determina el orden de los métodos de descriptor de acceso get y set en la interfaz de respaldo.

El descriptor de acceso get corresponde a un método sin parámetros con un valor devuelto del tipo de propiedad: el captador de propiedades.

Un descriptor de acceso set corresponde a un método con un único parámetro denominado valor, y ningún tipo de valor devuelto, el establecedor de propiedades.

Por lo tanto, estas dos declaraciones producen interfaces binarias diferentes.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Propiedades estáticas e de instancia

De forma similar a los métodos, MIDL 3.0 admite tanto propiedades de instancia como propiedades estáticas. Las propiedades estáticas se declaran con el modificador static prefijo y las propiedades de instancia se declaran sin él.

Métodos

Un método es un miembro que implementa un cálculo o una acción que puede realizar una instancia de la clase o por la propia clase. Se accede a un método estático a través de la clase . Se accede a un método de instancia de a través de una instancia de la clase .

Un método tiene una lista (posiblemente vacía) de parámetros , que representan valores o referencias de variables que se pasan al método . Un método también tiene un tipo de valor devuelto , que especifica el tipo del valor calculado y devuelto por el método . El tipo de valor devuelto de un método se void si no devuelve un valor.

// 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 de un método debe ser única en la clase en la que se declara el método. La firma de un método consta del nombre del método, los tipos de sus parámetros o el número de sus parámetros. La firma de un método no incluye el tipo de valor devuelto.

Modificadores de visibilidad del método

Un método puede tener uno de dos modificadores de visibilidad opcionales cuando el método está presente en una clase derivada.

El modificador reemplazable indica que un método (con el mismo nombre y firma) que pertenece a una subclase puede invalidar este método.

El modificador protegido indica que solo los miembros de una clase derivada pueden acceder a este método.

Sobrecarga de métodos

El método sobrecarga permite que varios métodos de la misma clase tengan el mismo nombre, siempre y cuando sus parámetros difieren en número (es decir, los métodos tienen diferentes aridad).

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

Nota

Todos los métodos con el mismo nombre deben tener diferencias aridad. Esto se debe a que los lenguajes de programación de tipo débil no admiten sobrecargas por tipo.

Parámetros

parámetros se usan para pasar valores o referencias de variables a un método. Un parámetro describe una ranura con un tipo y un nombre y, opcionalmente, alguna palabra clave modificadora. Un argumento es un valor real pasado en esa ranura desde el autor de la llamada del método al destinatario.

Los parámetros de un método obtienen su valor del argumento específico que se especifica cuando se invoca el método. La forma en que se pasan los argumentos entre el autor de la llamada y el destinatario depende del tipo del parámetro . De forma predeterminada, todos los parámetros son parámetros de entrada, es decir, se serializa desde el autor de la llamada solo al destinatario. Las palabras clave del modificador ref, ref consty out se pueden agregar para modificar la dirección predeterminada de serialización entre llamador y destinatario, y crear parámetros de salida. Sin embargo, no todas las palabras clave son válidas con todos los tipos de parámetro; Las combinaciones válidas se detallan a continuación.

Importante

Common Language Runtime (CLR) tiene conceptos y palabras clave modificadores que pueden parecer similares a los descritos en esta sección. Sin embargo, en la práctica no están relacionados, y el efecto de estos modificadores es específico del diseño y el funcionamiento de Windows Runtime.

Los tipos de valor se parámetros de entrada implícitamentey, de forma predeterminada, se pasa una copia del argumento desde el autor de la llamada al destinatario. Los parámetros de valor se pueden transformar en parámetros de salida con la palabra clave out; en ese caso, el argumento se serializa en su lugar desde el destinatario solo al autor de la llamada.

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

Como optimización especial del rendimiento, los tipos de estructura (y ningún otro tipo), que normalmente se pasan por valor como una copia completa, se pueden pasar por puntero a la estructura inmutable. Esto se logra con la palabra clave ref const (noconst ref), que marca el parámetro struct como parámetro de entrada, pero indica al serializador que pase un puntero al almacenamiento del struct, en lugar de pasar una copia completa de la estructura. Tenga en cuenta, sin embargo, que la estructura es inmutable; el puntero es conceptualmente un puntero const . No hay ningún boxeo implicado. Esta es una opción práctica al aceptar un valor tan grande como un Matrix4x4, por ejemplo.

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

Los tipos de referencia también son parámetros de entrada implícitamente, lo que significa que el autor de la llamada es responsable de asignar el objeto y pasar una referencia a él como argumento; sin embargo, dado que el argumento es una referencia al objeto , el autor de la llamada observa las modificaciones en ese objeto después de la llamada. Como alternativa, un tipo de referencia se puede realizar un parámetro de salida con la palabra clave out. En ese caso, los roles se invierten; el destinatario es el que asigna el objeto y lo devuelve al autor de la llamada. De nuevo, las palabras clave de ref no se pueden usar en general con tipos de referencia (consulte la excepción siguiente).

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

En la tabla siguiente se resume el comportamiento de las palabras clave de serialización de parámetros de valor y parámetros de referencia:

Comportamiento Asignado por Palabra clave Tipos Observaciones
Parámetro de entrada Llamador (ninguno) Todos los tipos Comportamiento predeterminado
ref const Solo estructura Optimización del rendimiento
Parámetro de salida Destinatario out Todos los tipos

Windows Runtime admite tipos de matriz, cuyo comportamiento como parámetro es algo diferente. Una matriz es una estructura de datos que contiene una serie de variables almacenadas secuencialmente y a las que se accede a través de un índice. Las variables contenidas en una matriz , también denominadas elementos de la matriz, son todos del mismo tipo y este tipo se denomina de tipo de elemento de la matriz.

MIDL 3.0 admite declaraciones de una matriz unidimensional .

Un parámetro de matriz es un tipo de referencia y, como todos los tipos de referencia, es de forma predeterminada un parámetro de entrada. En ese caso, el autor de la llamada asigna la matriz al destinatario, que puede leer sus elementos, pero no modificarlos (solo lectura). Esto se denomina patrón matriz de paso. Como alternativa, se puede usar el patrón matriz de relleno agregando la palabra clave al parámetro ; en esa configuración, el autor de la llamada sigue asignando la matriz, pero conceptualmente es un parámetro de salida en el sentido de que el destinatario rellenará los valores de los elementos de matriz. Por último, el último patrón es el matriz de recepción donde (como todos los parámetros de referencia de salida) el destinatario asigna e inicializa el argumento antes de que se devuelva al autor de la llamada.

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

En la tabla siguiente se resume el comportamiento de las matrices y sus elementos:

Patrón de matriz Palabra clave Asignado por Acceso a elementos por destinatario
"Pasar matriz" (ninguno) Llamador Solo lectura
"Matriz de relleno" ref Llamador Solo escritura
"Matriz de recepción" out Destinatario Lectura y escritura

Para obtener más información sobre el uso de parámetros de matriz de estilo C (también conocidos como matrices conformes) con C++/WinRT, consulta Parámetros de matriz.

Métodos estáticos e de instancia

Un método declarado con un modificador static prefijo es un método estático . Un método estático no tiene acceso a una instancia específica y, por tanto, solo puede acceder directamente a otros miembros estáticos de la clase.

Un método declarado sin un modificador static es un método de instancia de . Un método de instancia tiene acceso a una instancia específica y puede acceder tanto a los miembros estáticos como a los miembros de instancia de la clase .

La siguiente clase Entity tiene miembros estáticos e de instancia.

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

Cada instancia de Entity contiene su propio número de serie (y presumiblemente alguna otra información que no se muestra aquí). Internamente, el constructor Entity (que es como un método de instancia) inicializa la nueva instancia con el siguiente número de serie disponible.

La propiedad SerialNo proporciona acceso al número de serie de la instancia en la que se invoca la propiedad obtener método.

Los métodos estáticos GetNextSerial No y SetNextSerialNo pueden acceder al número de serie interno siguiente número de serie disponible miembro estático de la clase entity de .

Métodos invalidables y protegidos

Todos los métodos de un tipo de Windows Runtime son virtuales de forma eficaz. Cuando se invoca un método virtual, el tipo en tiempo de ejecución de la instancia para la que se realiza esa invocación determina la implementación del método real que se va a invocar.

Un método se puede invalidar en una clase derivada. Cuando una declaración de método de instancia incluye un modificador overridable, el método se puede invalidar mediante clases derivadas. Si una clase derivada invalida realmente un método de clase base invalidable viene determinado por la implementación; no está presente en los metadatos. Si una clase derivada vuelve a declarar un método en la clase base, declara un nuevo método que se encuentra junto con el método de clase derivada, en lugar de reemplazarlo.

Cuando una declaración de método de instancia incluye un modificador protected, el método solo es visible para las clases derivadas.

Eventos

Una declaración de evento es un miembro que especifica que una clase es un origen de eventos. Este origen de eventos proporciona notificaciones a cualquier destinatario que implemente un delegado (un método con una firma específica).

Se declara un evento con la palabra clave event, seguido del nombre del tipo de delegado (que describe la firma del método necesaria), seguido del nombre del evento. Este es un evento de ejemplo que usa un tipo de delegado existente de la plataforma.

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

Una declaración de evento agrega implícitamente dos métodos a la clase : un agregar método, que un cliente llama a para agregar un controlador de eventos al origen y un quitar método, que un cliente llama a para quitar un controlador de eventos agregado anteriormente. Estos son más ejemplos.

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

Por convención, siempre se pasan dos parámetros a un controlador de eventos de Windows Runtime: la identidad del remitente y un objeto de argumentos de evento. El remitente es el objeto que generó el evento o null para eventos estáticos. Si el evento no tiene ninguna carga significativa, los argumentos del evento son Object cuyo valor es NULL.

Delegados

Un tipo de delegado especifica un método con una lista de parámetros determinada y un tipo de valor devuelto. Una sola instancia de un evento puede contener cualquier número de referencias a instancias de su tipo delegado. La declaración es similar a la de un método miembro normal, salvo que existe fuera de una clase en tiempo de ejecución, y tiene el prefijo con la palabra clave delegate.

Un delegado permite tratar los métodos como entidades que se pueden asignar a variables y pasar como parámetros. Los delegados son similares al concepto de punteros de función que se encuentran en otros lenguajes. Pero, a diferencia de los punteros de función, los delegados están orientados a objetos y seguros para tipos.

Si no queremos usar el WindowSizeChangedEventHandler tipo de delegado de la plataforma, podemos definir nuestro propio tipo de delegado.

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

Una instancia de nuestro SizeChangedHandler tipo de delegado puede hacer referencia a cualquier método que tome dos argumentos (un objeto y un WindowSizeChangedEventArgs) y devuelva void. Después de analizar structs, también podrá reemplazar el parámetro WindowSizeChangedEventArgs por un evento tipo de argumentos propios.

Una propiedad interesante y útil de un delegado es que no conoce ni se preocupa por la clase del método al que hace referencia; todo lo que importa es que el método al que se hace referencia tenga los mismos parámetros y el mismo tipo de valor devuelto que el delegado.

Opcionalmente, puede asignar una declaración de delegado con [uuid(...)].

Consulte también Delegados que devuelven hrESULT.

Estructuras

Un estructura es una estructura de datos que puede contener miembros de datos (campos). Pero, a diferencia de una clase, un struct es un tipo de valor.

Las estructuras son especialmente útiles para estructuras de datos pequeñas que tienen semántica de valor. Números complejos, o puntos en un sistema de coordenadas, son buenos ejemplos de estructuras. El uso de estructuras en lugar de clases para estructuras de datos pequeñas puede marcar una gran diferencia en el número de asignaciones de memoria que realiza una aplicación.

Vamos a usar un ejemplo para contrastar clases y estructuras. Esta es una versión de Point primero como una clase .

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

Este programa de C# crea e inicializa una matriz de 100 instancias de Point. Con Point implementado como una clase, se crean instancias de 101 objetos independientes: uno para el propio objeto de matriz; y uno para cada uno de los 100 elementos Point.

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

Una alternativa más eficaz es convertir Point una estructura, en lugar de una clase.

struct Point
{
    Int32 x;
    Int32 y;
};

Ahora, solo se crea una instancia de un objeto: el propio objeto de matriz. Los elementos point point se almacenan en línea dentro de la matriz; una disposición de memoria que las memorias caché del procesador pueden usar para un efecto eficaz.

Cambiar una estructura es un cambio importante binario. Por lo tanto, las estructuras implementadas como parte de Windows en sí no se modifican una vez introducidas.

Interfaces

Una interfaz define un contrato que las clases pueden implementar. Una interfaz puede contener métodos, propiedades y eventos, al igual que las clases.

A diferencia de una clase, una interfaz no proporciona implementaciones de los miembros que define. Simplemente especifica los miembros que deben proporcionar cualquier clase que implemente la interfaz.

Las interfaces pueden requerir una clase que implemente la interfaz para implementar también otras interfaces. En el ejemplo siguiente, la interfaz IComboBox requiere que cualquier clase que implemente IComboBox, también implemente ITextBox y IListBox. Además, una clase que implementa IComboBox también debe implementar IControl. Esto se debe a que ITextBox y requieren .

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 clase puede implementar cero o más interfaces. En el ejemplo siguiente, la clase EditBox implementa tanto IControl como IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Para los tipos de Windows Runtime en la plataforma Windows, se define una interfaz si se espera que los desarrolladores que consumen esos tipos implementen la interfaz. Otro caso de uso para definir una interfaz es cuando varias clases en tiempo de ejecución implementan la interfaz, y los desarrolladores que consumen esas clases en tiempo de ejecución tendrán acceso a diferentes tipos de objeto genéricamente (y, por lo tanto, polimórficamente) a través de esa interfaz común.

Nota

Piense dos veces en usar la palabra clave requires en MIDL 3.0. Puede provocar diseños desordenados, especialmente cuando se tiene en cuenta el control de versiones.

Enumeraciones

Un tipo de enumeración (o tipo enumerado o enumeración) es un tipo de valor distinto con un conjunto de constantes con nombre. En el ejemplo siguiente se define y se usa un tipo de enumeración denominado Color con tres valores constantes: Red, Greeny Blue.

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

Cada tipo de enumeración tiene un tipo entero correspondiente denominado tipo subyacente del tipo de enumeración. El tipo subyacente de una enumeración es int32 o UInt32.

Windows Runtime admite dos tipos de enumeraciones: enumeraciones de normales y marcas de enumeraciones. Una enumeración del tipo normal expresa un conjunto de valores exclusivos; mientras que uno de los tipos de marcas representa un conjunto de valores booleanos. Para habilitar operadores bit a bit para una enumeración de marcas, el compilador MIDL 3.0 genera sobrecargas de operador de C++.

Una enumeración flags tiene aplicado el atributo [flags]. En ese caso, el tipo subyacente de la enumeración es UInt32. Cuando el atributo [flags] no está presente (una enumeración normal), el tipo subyacente de la enumeración es Int32. No es posible declarar una enumeración como cualquier otro tipo.

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

El formato de almacenamiento y el intervalo de valores posibles de un tipo de enumeración están determinados por su tipo subyacente. El conjunto de valores que un tipo de enumeración puede asumir no está limitado por sus miembros de enumeración declarados.

En el ejemplo siguiente se define un tipo de enumeración denominado Alignment, con un tipo subyacente de Int32.

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

Como también es true para C y C++, una enumeración MIDL 3.0 puede incluir una expresión constante que especifica el valor del miembro (como se ha visto anteriormente). El valor constante de cada miembro de enumeración debe estar en el intervalo del tipo subyacente de la enumeración. Cuando una declaración de miembro de enumeración no especifica explícitamente un valor, el miembro recibe el valor cero (si es el primer miembro del tipo de enumeración) o el valor del miembro de enumeración anterior textualmente más uno.

En el ejemplo siguiente se define un tipo de enumeración denominado Permisos, con un tipo subyacente de UInt32.

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

Atributos

Los tipos, miembros y otras entidades del código fuente MIDL 3.0 admiten modificadores que controlan determinados aspectos de su comportamiento. Por ejemplo, la accesibilidad de un método se controla mediante el modificador de acceso protected. MIDL 3.0 generaliza esta funcionalidad de modo que los tipos definidos por el usuario de información declarativa se pueden adjuntar a entidades de programa y recuperarse en tiempo de ejecución de los metadatos.

Los programas especifican esta información declarativa adicional definiendo y usando atributos .

En el ejemplo siguiente se define un atributo HelpAttribute, que se puede colocar en entidades de programa para proporcionar vínculos a su documentación asociada. Como puede ver, un atributo es básicamente un tipo de estructura, por lo que no tiene un constructor y solo contiene miembros de datos.

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

Para aplicar un atributo, asigne su nombre, junto con cualquier argumento, entre corchetes justo antes de la declaración asociada. Si el nombre de un atributo termina en Atributo, esa parte del nombre se puede omitir cuando se hace referencia al atributo. Por ejemplo, el atributo HelpAttribute se puede usar de esta manera.

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

Puede aplicar el mismo atributo a varias declaraciones mediante un bloque de ámbito que sigue al atributo . Es decir, un atributo seguido inmediatamente de llaves que rodean las declaraciones a las que se aplica el atributo.

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

Los atributos implementados como parte de Windows en sí suelen estar en el espacio de nombres Windows.Foundation.

Como se muestra en el primer ejemplo, se usa el atributo [attributeusage(<target>)] en la definición de atributo. Los valores de destino válidos son target_all, target_delegate, target_enum, target_event, target_field, target_interface, target_method, target_parameter, target_property, target_runtimeclassy target_struct. Puede incluir varios destinos entre paréntesis, separados por comas.

Otros atributos que puede aplicar a un atributo son [allowmultiple] y [attributename("<name>")].

Tipos con parámetros

En el ejemplo siguiente se produce error MIDL2025: [msg]error de sintaxis [context]: esperando > o, cerca de ">>".

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

En su lugar, inserte un espacio entre los dos caracteres > para que el par de caracteres de cierre de plantillas no se interprete mal como un operador de desplazamiento a la derecha.

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

En el ejemplo siguiente se produce error MIDL2025: [msg]error de sintaxis [context]: esperando > o, cerca de "[". Esto se debe a que no es válido usar una matriz como argumento de tipo de parámetro en una interfaz parametrizada.

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

Para la solución, consulte Devolver una matriz de forma asincrónica.