Presentar la interfaz de clase
La interfaz de clase, que no se define explícitamente en el código administrado, es una interfaz que expone todos los métodos, propiedades, campos y eventos públicos que se exponen explícitamente en el objeto .NET. Puede ser una interfaz dual o sólo de envío. La interfaz de clase recibe el nombre de la propia clase .NET, precedida por un carácter de subrayado. Por ejemplo, para la clase Mammal, la interfaz de clase es _Mammal.
Para las clases derivadas, la interfaz de clase también expone todos los métodos, propiedades y campos públicos de la clase base. La clase derivada expone asimismo una interfaz de clase para cada clase base. Por ejemplo, si la clase Mammal extiende la clase MammalSuperclass, que a su vez extiende System.Object, el objeto .NET expone a los clientes COM tres interfaces de clase, denominadas _Mammal, _MammalSuperclass y _Object.
Por ejemplo, observe la siguiente clase .NET:
' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
Sub Eat()
Sub Breathe()
Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
void Eat();
void Breathe():
void Sleep();
}
El cliente COM puede obtener un puntero a una interfaz de clase denominada _Mammal, descrita en la biblioteca de tipos que genera la herramienta Exportador de la biblioteca de tipos (Tlbexp.exe). Si la clase Mammal ha implementado una o más interfaces, dichas interfaces aparecerán bajo la coclase.
[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
pRetVal);
[id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
VARIANT_BOOL* pRetVal);
[id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x6002000d)] HRESULT Eat();
[id(0x6002000e)] HRESULT Breathe();
[id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
[default] interface _Mammal;
}
Generar la interfaz de clase es una acción opcional. De manera predeterminada, la interoperabilidad COM genera una interfaz sólo de envío para cada clase que se exporta a una biblioteca de tipos. La creación automática de esta interfaz se puede impedir o modificar aplicando ClassInterfaceAttribute a la clase. Si bien la interfaz de clase puede facilitar la tarea de exponer clases administradas a COM, sus usos son limitados.
Precaución |
---|
Si se usa la interfaz de clase en lugar de definir una interfaz propia de manera explícita, el control de las futuras versiones de la clase administrada puede ser más complicado.Antes de usar la interfaz de clase lea las instrucciones siguientes. |
Defina una interfaz explícita para que la usen los clientes COM en lugar de generar la interfaz de clase.
Como la interoperabilidad COM genera una interfaz de clase automáticamente, los cambios en versiones posteriores de la clase pueden modificar el diseño de la interfaz de clase expuesta por Common Language Runtime. Puesto que los clientes COM no están preparados normalmente para controlar cambios en el diseño de una interfaz, se interrumpen si se cambia el diseño de miembro de la clase.
Esta instrucción enfatiza la idea de que las interfaces expuestas a los clientes COM no se deben modificar. Para reducir el riesgo de que los clientes COM se interrumpan por volver a ordenar involuntariamente el diseño de interfaz, aísle del diseño de interfaz todos los cambios que se hagan en la clase definiendo las interfaces de manera explícita.
Use ClassInterfaceAttribute para desactivar la generación automática de la interfaz de clase e implemente una interfaz explícita para la clase, como se muestra en el siguiente fragmento de código:
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit {
void M();
}
El valor ClassInterfaceType.None impide que la interfaz de clase se genere cuando los metadatos de la clase se exportan a una biblioteca de tipos. En el ejemplo anterior, los clientes COM sólo pueden tener acceso a la clase LoanApp mediante la interfaz IExplicit.
No almacene identificadores de envío (DispId) en caché.
La utilización de la interfaz de clase es una opción aceptable para los clientes con scripts, los clientes Microsoft Visual Basic 6.0 o cualquier cliente de tiempo de ejecución que no almacene en caché los identificadores DispId de los miembros de la interfaz. Los identificadores DispId identifican los miembros de la interfaz para admitir enlaces en tiempo de ejecución.
Para la interfaz de clase, la generación de identificadores DispId se basa en la posición de los miembros en la interfaz. Si se cambia el orden de los miembros y se exporta la clase a una biblioteca de tipos, se modificarán los identificadores DispId generados en la interfaz de clase.
Para evitar que los clientes COM de tiempo de ejecución se interrumpan al utilizar la interfaz de clase, aplique ClassInterfaceAttribute con el valor ClassInterfaceType.AutoDispatch. Este valor implementa una interfaz de clase de sólo envío, pero omite la descripción de la interfaz de la biblioteca de tipos. Sin una descripción de la interfaz, los clientes no pueden almacenar en caché los identificadores DispId en tiempo de compilación. Aunque este es el tipo predeterminado de la interfaz de clase, se puede aplicar el valor del atributo de forma explícita.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch]
public class LoanApp : IAnother {
void M();
}
Para obtener el DispId de un miembro de interfaz en tiempo de ejecución, los clientes COM pueden llamar a IDispatch.GetIdsOfNames. Para invocar un método en la interfaz, pase el DispId devuelto a IDispatch.Invoke como un argumento.
Restrinja el uso de la opción de interfaz dual en la interfaz de clase.
Las interfaces duales permiten los enlaces en tiempo de diseño y en tiempo de ejecución de los clientes COM con los miembros de interfaz. El establecimiento de la interfaz de clase en dual puede ser útil en tiempo de diseño y al hacer pruebas. Esta opción también es aceptable para una clase administrada (y sus clases base) que no se va a modificar nunca. En todos los demás casos, no es necesario establecer la interfaz de clase en dual.
Una interfaz dual generada automáticamente puede ser recomendable en algunas ocasiones, pero a menudo genera complicaciones de control de versiones. Por ejemplo, los clientes COM que usen la interfaz de clase de una clase derivada se pueden interrumpir con facilidad si se hacen cambios en la clase base. Si la clase base es de terceros, el usuario no tiene control sobre el diseño de la interfaz de clase. Además, a diferencia de las interfaces de sólo envío, las interfaces duales (ClassInterface.AutoDual) proporcionan una descripción de la interfaz de clase en la biblioteca de tipos exportada. Dicha descripción hace que los clientes de tiempo de ejecución almacenen en caché los identificadores DispId en tiempo de ejecución.