Share via


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 Windows Runtime tipos dentro de archivos de Lenguaje de definición de interfaz (IDL) (.idl archivos). Esta nueva sintaxis se sentirá familiar con cualquier persona 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. 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 /winrt modificador).

Nota

Consulte también la referencia consolidada Windows Runtime (el sistema de tipos de Windows Runtime y los archivos de metadatos de Windows).

MIDL 1.0, 2.0 y 3.0

El lenguaje de definición de interfaz (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 de MIDL 2.0 actualizada (también conocida como MIDLRT) se desarrolló en Microsoft para declarar Windows Runtime API para la plataforma Windows. Si busca en la carpeta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt windows SDK, verá ejemplos de .idl archivos escritos con la sintaxis MIDL 2.0. Se trata de API de Windows Runtime integradas, declaradas en su forma de interfaz binaria de aplicación (ABI). Estos archivos existen principalmente para herramientas que se usarán: 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 Windows Runtime API. Y puede usarlo en los proyectos, especialmente para definir clases en tiempo de ejecución de C++/WinRT . Los encabezados, para su uso desde C++/WinRT, para las API de Windows Runtime integradas 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 Windows Runtime. Esto se hace, en parte, eligiendo pasar exclusivamente tipos de Windows Runtime hacia y desde Windows Runtime API. Aunque es una decisión de diseño válida para pasar una interfaz COM sin procesar hacia y desde una API de Windows Runtime, esto limita los consumidores de esa API de Windows Runtime particular a las 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 puntero de interfaz IDXGISwapChain y, a continuación, pasarlo al método ISwapChainPanelNative::SetSwapChain. Por ejemplo, una aplicación de C# no podría obtener un IDXGISwapChain para 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 hay características o funcionalidades de un componente COM que desea exponer a Windows Runtime proyecciones de lenguaje más allá de C++, puede crear un componente de Windows Runtime de C++ (WRC) que cree y use directamente el componente COM (como DirectX, por ejemplo), y expone una replicación de algún subconjunto de sus características y funcionalidades en forma de un componente COM (por ejemplo, DirectX). Windows Runtime superficie de API que solo toma y devuelve Windows Runtime tipos. A continuación, podría consumir ese WRC desde una aplicación escrita en cualquier proyección de lenguaje Windows Runtime.

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

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

  • Las clases, las interfaces, las estructuras y las 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 Windows Runtime (normalmente un .winmd archivo).

// 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 Windows Runtime se convierte en 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 título) y dos métodos, denominados Equals y ApplyDiscount. Tenga en cuenta el uso del tipo Single en lugar de float. Y esa cadena tiene una "S" mayúscula.

Sugerencia

Visual Studio proporciona la mejor experiencia para compilar MIDL 3.0, mediante la extensión de Visual Studio (VSIX) de C++/WinRT. 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 midl.exe herramienta compila el ejemplo y genera un archivo de metadatos denominado Bookstore.winmd (de forma predeterminada, se usa el nombre del .idl archivo).

Sugerencia

Si usa más de un archivo IDL (para obtener consejos sobre eso, consulte Factoring runtime classes into Midl files (.idl)), combine todos los archivos resultantes .winmd en un único archivo con el mismo nombre que el espacio de nombres raíz. Ese archivo final .winmd será el que hará referencia a los consumidores de las API.

En este caso, BookSku es la única clase en tiempo de ejecución en el espacio de nombres Bookstore , por lo que guardamos un paso y acabamos de asignar un nombre al .idl archivo para el espacio de nombres.

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

where midl

Si desea usar los tipos definidos en un .idl archivo de otro .idl archivo, use la import directiva . Para obtener más información y un ejemplo de código, consulta Controles XAML; enlazar a una propiedad C++/WinRT. Por supuesto, si está consumiendo un componente integrado o de terceros, no tendrá acceso al .idl archivo. Por ejemplo, es posible que quiera consumir la API de Windows Runtime Win2D para la representación de gráficos 2D en modo inmediato. El comando anterior usó el /reference modificador 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 Bookstore.winmda .

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. Prefijo 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 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 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 se pueden usar alias para estos tipos.

Category Descripción
Tipos de valor Tipos simples Integral firmado: Int16, Int32, Int64
Integral 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
Punto flotante IEEE: Single, Double
Boolean: booleano
UUID de 128 bits: Guid
Tipos de enumeración Tipos definidos por el usuario de la enumeración de formulario E {...}
Tipos de estructura Tipos definidos por el usuario del formulario S {...}
Tipos que aceptan valores NULL Extensiones de todos los demás tipos de valor con un valor NULL
Tipos de referencia Tipos de clase Clase base ultimate de todos los demás tipos: Object
Tipos definidos por el usuario de la clase runtime de formulario C {...}
Tipos de interfaz Tipos definidos por el usuario de la interfaz de formulario I {...}
Tipos delegados Tipos definidos por el usuario del delegado <de formulario 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 IEEE 754 de precisión sencilla y de precisión doble de 32 bits, respectivamente.

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

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

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

Category Bits Tipo Rango/precisión
Integral 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
Punto flotante 32 Single 1,5 × de 10 a 45 a 3,4 × 1038, precisión de 7 dígitos
64 Double 5,0 × de 10 a 324 a 1,7 × 10308, precisión de 15 dígitos

Los archivos de origen MIDL 3.0 usan definiciones de tipo 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 delegados y
  • tipos de enumeración.

Un tipo de atributo define un atributo 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 de estructura define una estructura 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 tipo de interfaz define una interfaz 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 Windows Runtime IInspectable.

Un tipo runtimeclass define una clase 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 tipo de delegado define un delegado Windows Runtime, que representa una referencia a un método con una lista de parámetros y un tipo de valor devuelto determinados. 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 de 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
  • el tipo de objeto .

No es necesario declarar una matriz unidimensional antes de poder usarla. En su lugar, los tipos de matriz se crean mediante un nombre de tipo entre corchetes. Por ejemplo, Int32[] es una matriz unidimensional de Int32.

De forma similar, los tipos de valor que aceptan valores NULL tampoco tienen que definirse antes de que se puedan usar. 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 nulladicional . Por ejemplo, Windows.Foundation.IReference<Int32> es un tipo que puede contener cualquier entero de 32 bits o el valor null. Vea también IReference<T>.

Por último, MIDL 3.0 admite el tipo object, que se asigna a la interfaz IInspectable de Windows Runtime. Los tipos de referencia interface y runtimeclass derivan conceptualmente del tipo Object ; delegate 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. 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, su precedencia 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 precedencia.

Categoría Expresión Descripción
Principal x++ Postincremento
x-- Postdecremento
Unario +x Identidad
-X Negación
!x Negación lógica
~x Negación bit a bit
++x Preincremento
--x Predecremento
Multiplicativa x * y Multiplicación
x / y División
x % y Resto
Aditivo x + y Adición, concatenación de cadenas, combinación de delegados
x : y Resta, eliminación de delegados
Mayús x << y Desplazamiento a la izquierda
x >> y Desplazamiento a la derecha
AND bit a bit x & y Entero bit a bit AND
XOR bit a bit x ^ y XOR entero bit a bit
OR bit a bit x | y OR bit a bit entero
Y lógico x && y AND lógico booleano
O lógico x || y OR lógico booleano

Clases

Las clases (o clases en tiempo de ejecución) son las más fundamentales de los tipos 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 herencia y polimorfismo: mecanismos en los que las 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 runtimeclass palabra clave , 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 propiedades de lectura y escritura de Int32denominadas Height y Width, y una propiedad estática de solo lectura denominada NumberOfAreas.

De forma predeterminada, una clase runtime 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 C++/WinRT para más información.

Puede declarar que una clase no admite instancias (y, por lo tanto, solo debe contener miembros estáticos) mediante el prefijo de la definición de clase en tiempo de ejecución con la static palabra clave . 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 partial palabra clave . 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ática La clase no tiene instancias. Por lo tanto, solo se permiten miembros estáticos.
partial La definición de clase está incompleta.

Consulte Composición y activación para 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 públicos implícitamente. 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

De hecho, las clases del 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 composable . Las clases que admiten composición tienen restricciones. Para que una aplicación pase las pruebas del kit de certificación de aplicaciones en Windows que Visual Studio y Microsoft Store utilizan para validar los envíos (y para que la aplicación se incorpore correctamente a Microsoft Store), una clase que admite composición debe derivar en última instancia de una clase base de Windows. Eso significa que, en la raíz misma de la jerarquía de herencia, la clase debe ser un tipo que se origina en un espacio de nombres Windows.*.

Consulta Controles XAML; enlazar a una propiedad C++/WinRT para más información.

En el ejemplo siguiente, la clase base de Volume es Area y 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í, El área y el volumen se definen en el mismo archivo de código fuente. Para obtener una explicación de las ventajas y desventajas, consulte Factorización de clases en tiempo de ejecución en archivos Midl (.idl).

Una clase hereda a 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 aquellos de 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, Width y 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 escrito si Area y Volume 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 que siguen a la clase base (opcional).

En el ejemplo siguiente, la clase Area implementa la interfaz IStringable ; y la clase Volume implementa tanto IStringable como la hipotética interfaz 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 se declaran los miembros de la interfaz en la clase . Por supuesto, tiene que 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 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 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 devuelto) y con el mismo nombre que la clase contenedora.

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

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 Los métodos siguientes.

Se heredan las propiedades, los métodos y los eventos de instancia. 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 no sesealadas. Una clase no sealada puede tener uno o varios constructores protegidos.

Propiedades

Las propiedades son conceptualmente similares a los campos (por ejemplo, campos de C#; o los campos de una estructura MIDL 3.0). Tanto las propiedades como los campos son miembros con un nombre y un tipo asociado. Pero 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 el campo de un struct, salvo que la declaración termina con una get palabra clave o una set palabra clave escrita entre los delimitadores { y }, y finalizando en un punto y coma.

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

Por ejemplo, la clase Area, que se ha visto 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 get palabras clave y set . La omisión implica que la propiedad es de lectura y escritura, y es semánticamente idéntica a proporcionar las get palabras clave y set en ese orden,get seguida de set.

Además, solo puede especificar la get palabra clave 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; };

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

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

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

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

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

En ese caso, agregue la palabra clave property set 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 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 get descriptor de acceso corresponde a un método sin parámetros con un valor devuelto del tipo de propiedad, el captador de propiedades.

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

Por lo tanto, estas dos declaraciones generan 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 propiedades de instancia y propiedades estáticas. Las propiedades estáticas se declaran con el static prefijo modificador 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 obtiene acceso a un método estático a través de la clase . Se obtiene acceso a un método de instancia 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 es 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 signatura 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, de los tipos de sus parámetros o del número de sus parámetros. La signatura de un método no incluye el tipo de valor devuelto.

Modificadores de visibilidad del método

Un método puede tener uno de los 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) puede invalidar este método que pertenece a una subclase.

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

Sobrecarga de métodos

La sobrecarga de métodos 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 una aridad diferente).

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 una aridad diferente. Esto se debe a que los lenguajes de programación poco tipados no admiten sobrecargas por tipo.

Parámetros

Los 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 del 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 calculan las referencias del autor de la llamada solo al autor de la llamada. Las palabras clave refmodificadora , ref consty out se pueden agregar para modificar la dirección predeterminada de serialización entre el autor de la llamada y el 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 las descritas 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 la Windows Runtime.

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

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

Como optimización de rendimiento especial, los tipos struct (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 ref const palabra clave (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. Sin embargo, tenga en cuenta que la estructura es inmutable; el puntero es conceptualmente un puntero const. No hay boxeo implicado. Esta es una opción práctica al aceptar un valor tan grande como 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 pasarle una referencia 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 out palabra clave . En ese caso, los roles se invierten; el autor de la llamada es el que asigna el objeto y lo devuelve al autor de la llamada. De nuevo, las ref palabras clave no se pueden usar en general con tipos de referencia (vea 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 Autor de llamada (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 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, al igual que 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 puede modificarlos (solo lectura). Esto se denomina patrón de matriz de paso . Como alternativa, el patrón de matriz de relleno se puede usar agregando la ref 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 la matriz. Por último, el último patrón es la matriz de recepción donde (como todos los parámetros de referencia de salida) el destinatario está asignando e inicializando 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) Autor de llamada Solo lectura
"Matriz de relleno" ref Autor de llamada Solo escritura
"Matriz de recepción" out Destinatario Lectura-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 y de instancia

Un método declarado con un static prefijo modificador 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. Un método de instancia tiene acceso a una instancia específica y puede acceder a los miembros estáticos y 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 el método get de la propiedad .

Los métodos estáticos GetNextSerialNo y SetNextSerialNo pueden tener acceso al siguiente miembro estático del número de serie disponible interno de la clase Entity .

Métodos invalidables y protegidos

Todos los métodos de un tipo 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 tiene lugar esa invocación determina la implementación del método real que se invocará.

Un método se puede invalidar en una clase derivada. Cuando una declaración de método de instancia incluye un overridable modificador, el método se puede invalidar mediante clases derivadas. Si una clase derivada invalida realmente un método de clase base reemplazable 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 invalidarlo.

Cuando una declaración de método de instancia incluye un protected modificador, 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 mediante la event palabra clave , 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 método add , que un cliente llama a para agregar un controlador de eventos al origen y un método remove , 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 un objeto cuyo valor es NULL.

Delegados

Un tipo delegado especifica un método con una lista de parámetros determinada y un tipo de valor devuelto. Una única 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, excepto que existe fuera de una clase en tiempo de ejecución, y tiene el prefijo con la delegate palabra clave .

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 de 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 tipo de delegado WindowSizeChangedEventHandler de la plataforma, podemos definir nuestro propio tipo de delegado.

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

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

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 tiene los mismos parámetros y el tipo de valor devuelto que el delegado.

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

Consulte también Delegados que devuelven HRESULT.

Estructuras

Una 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.

Los structs son particularmente útiles para estructuras de datos pequeñas que tengan semánticas de valor. Los números complejos, o los puntos de 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 en primer lugar 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 en un struct, 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 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 proporcionarse por 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 e IListBox. Además, una clase que implementa IComboBox también debe implementar IControl. Esto se debe a que tanto ITextBox como IListBoxlo 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 IControl e IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Para Windows Runtime tipos en la plataforma Windows, se define una interfaz si se espera que los desarrolladores que consuman 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 tanto, polimórficamente) a través de esa interfaz común.

Nota

Piense dos veces en usar la requires palabra clave en MIDL 3.0. Puede dar lugar a 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: Rojo, Verde y Azul.

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.

El Windows Runtime admite dos tipos de enumeraciones: enumeraciones normales y enumeraciones de marcas. 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 [flags] atributo . En ese caso, el tipo subyacente de la enumeración es UInt32. Cuando el [flags] atributo 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 vienen 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 Permissions, 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 protected acceso. MIDL 3.0 generaliza esta funcionalidad de modo que los tipos definidos por el usuario de información declarativa se puedan adjuntar a entidades de programa y recuperarse en tiempo de ejecución de los metadatos.

Los programas especifican esta información declarativa adicional mediante la definición y el uso de 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 los argumentos, 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 en la [attributeusage(<target>)] definición de atributo. Los valores de destino válidos son target_all, , target_interfacetarget_fieldtarget_eventtarget_enumtarget_parametertarget_delegatetarget_method, 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 parametrizados

En el ejemplo siguiente se produce el 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 derecho.

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

En el ejemplo siguiente se produce el 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 con parámetros.

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

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