.net - que - Dada la elección, ¿cuáles son los pros/contras de los ensambles de modo mixto frente a los DLL de interoperabilidad separados?
extension dll (1)
Descargo de responsabilidad: para una respuesta definitiva, tendría que preguntarle a alguien del equipo de desarrollo, pero esta es mi mejor suposición.
En la configuración estándar, el ensamblado administrado intentará localizar y cargar la DLL nativa que necesita. Buscará en los directorios dependientes de la plataforma ( x86
o x64
). Luego cargará la DLL que encontró allí y procederá a lanzar interoperabilidad de P / Invoke.
Este es un procedimiento bastante estándar para la interoperación con bibliotecas nativas en .NET: el único código personalizado de System.Data.SQLite es el que intenta localizar el archivo DLL y carga la versión correcta. El resto es simple P / invocar. Pero incluso esta es una práctica común cuando se trata de bibliotecas.
La principal ventaja de este enfoque es que el usuario de la biblioteca puede construir su proyecto para la plataforma AnyCPU y la arquitectura del procesador se resolverá en tiempo de ejecución; todo funcionará como se espera, si se ejecuta en x86 o x64, siempre que ambas DLL nativas estén disponibles. . Y el autor de la biblioteca recibe menos solicitudes de soporte.
Vamos a compararlo con el enfoque de modo mixto. Una DLL de modo mixto tiene algunas desventajas, la principal es que debe ser específica de la plataforma. Entonces, si elige este enfoque, deberá vincular su aplicación a una plataforma particular. Si desea admitir tanto x86 como x64, deberá compilar versiones separadas, cada una de las cuales se vinculará a la versión correcta de System.Data.SQLite.
Si no entiendes esto perfectamente, entonces boom . Peor aún, si lo compila para la plataforma AnyCPU en su máquina de desarrollo x64, parecerá que funciona bien a primera vista, pero se implosionará en la antigua caja x86 de su cliente. Tener que lidiar con este tipo de problemas no es agradable, y la solución simple de usar DLL por separado resuelve el problema por completo.
La otra desventaja que puedo pensar es la incapacidad de cargar el ensamblaje desde la memoria, pero esto debería ser a lo sumo un pequeño inconveniente, ya que esto también se aplica a las DLL nativas.
En cuanto al GAC, creo que en el caso de ensamblajes nativos independientes, el código que los busca podría no ubicarlos en algunos casos, o podría encontrar una versión diferente de la DLL. El código en System.Data.SQLite intenta realmente localizar el archivo DLL nativo, pero no hay ningún problema con el archivo DLL de modo mixto en primer lugar, por lo que la falla no es una opción.
Sin embargo, dices:
Se siente más ordenado.
Echemos un vistazo más de cerca a esto. :)
System.Data.SQLite tiene un enfoque bastante inusual para la interoperabilidad de modo mixto. Usualmente, usaría C ++ / CLI para construir un ensamblaje de modo mixto. Esto le permite combinar código administrado y nativo del mismo proyecto de C ++ / CLI en una sola DLL y utiliza la llamada interoperabilidad de C ++ para manejar las llamadas desde el otro lado de la barrera gestionada / no gestionada. La ventaja de esto es que es más ligero y más rápido que P / Invoke, ya que puede evitar la mayoría de las referencias.
System.Data.SQLite hace algo diferente: construye su código C # en un netmodule , y luego usa el enlazador C ++ para vincular el netmodule con el código SQLite nativo. Esto da como resultado un ensamblaje de modo mixto.
Lo interesante es que, sin línea C ++ / CLI, C # no tiene un mecanismo directo para invocar el código nativo en el mismo ensamblaje en modo mixto, ya que C # realmente no estaba destinado a ser utilizado en ensambles de modo mixto en primer lugar. Entonces, el código C # de este ensamblado final simplemente P / Invocará . Sí, lo leiste bien. ¿Esto todavía se siente tan ordenado? Sin embargo, es un truco ingenioso. :)
Eche un vistazo al código en UnsafeNativeMethods.cs
:
#if PLATFORM_COMPACTFRAMEWORK
//
// NOTE: On the .NET Compact Framework, the native interop assembly must
// be used because it provides several workarounds to .NET Compact
// Framework limitations important for proper operation of the core
// System.Data.SQLite functionality (e.g. being able to bind
// parameters and handle column values of types Int64 and Double).
//
internal const string SQLITE_DLL = "SQLite.Interop.099.dll";
#elif SQLITE_STANDARD
//
// NOTE: Otherwise, if the standard SQLite library is enabled, use it.
//
internal const string SQLITE_DLL = "sqlite3";
#elif USE_INTEROP_DLL
//
// NOTE: Otherwise, if the native SQLite interop assembly is enabled,
// use it.
//
internal const string SQLITE_DLL = "SQLite.Interop.dll";
#else
//
// NOTE: Finally, assume that the mixed-mode assembly is being used.
//
internal const string SQLITE_DLL = "System.Data.SQLite.dll";
#endif
Si desea ver el proceso de compilación, eche un vistazo a los proyectos C ++ MSBuild. Aquí hay algunas opciones del enlazador que muestran el uso de netmodules (en <AdditionalDependencies>
):
<Link>
<AdditionalOptions>$(INTEROP_ASSEMBLY_RESOURCES) %(AdditionalOptions)</AdditionalOptions>
<AdditionalLibraryDirectories>$(INTEROP_LIBRARY_DIRECTORIES)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(ProjectDir)../bin/$(ConfigurationYear)/$(Configuration)Module/bin/System.Data.SQLite.netmodule $(INTEROP_LIBRARY_DEPENDENCIES);%(AdditionalDependencies)</AdditionalDependencies>
<Version>$(INTEROP_LINKER_VERSION)</Version>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateMapFile>true</GenerateMapFile>
<MapExports>true</MapExports>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<TargetMachine>MachineX64</TargetMachine>
<CLRUnmanagedCodeCheck>true</CLRUnmanagedCodeCheck>
<KeyFile>$(INTEROP_KEY_FILE)</KeyFile>
<DelaySign>true</DelaySign>
</Link>
Cuando se ofrece un componente de terceros en versiones de "modo mixto" y "dúo de interoperabilidad separado", ¿cuáles son los pros y los contras de cada uno?
Un buen ejemplo es System.Data.SQLite .
El enlace de arriba tiene esto para decir:
[Los paquetes de ensamblaje en modo mixto] solo se deben usar en casos en los que el ensamblaje binario se debe implementar en la memoria caché de ensamblaje global por algún motivo.
¿Pero por qué? El ensamblaje de modo mixto parece funcionar bien en mis proyectos, sin instalación de GAC (solo haga una copia en el directorio exe de la aplicación). Es bueno tener una DLL menos. Se siente más ordenado. Entonces, ¿cuáles son los contras?
Viceversa, ¿por qué debería / debería uno favorecer a la versión de dos DLL "native-dll + interop-dll"?