Estamos tratando de desarrollar una aplicación multiplataforma para equipos de escritorio (Windows y Mac) usando C #. La aplicación contiene una gran cantidad de material dependiente de la plataforma y el líder de nuestro equipo quiere que escribamos todo ese código en C #. La manera fácil de hacer esto sería escribir envoltorios en C ++ y simplemente hacer referencia a las bibliotecas en el código C # y dejar que las bibliotecas de C ++ se ocupen de las cosas de la plataforma ... por desgracia, eso no es así.
Estoy tratando de configurar una biblioteca con Visual Studio para Mac. Espero poder proporcionar una única interfaz en una sola biblioteca para otorgar acceso a la biblioteca de texto a voz nativa en la plataforma actual, preferiblemente sin hacer dos ensamblajes. La biblioteca de texto a voz en C # para Windows no está disponible en Mac, por lo que realmente no tenemos una opción alternativa que proporcionar un puente nosotros mismos.
Estoy contento de que la biblioteca realice búsquedas en el sistema operativo para determinar en qué sistema operativo se está ejecutando y / o intente cargar un montón de bibliotecas nativas y simplemente use cualquier biblioteca que se cargue.
Si debo hacerlo, tendré que hacer un solo proyecto que me permita escribir dos implementaciones. No he encontrado una manera de crear un proyecto de biblioteca en Visual Studio para Mac, pero eso me permitirá hacerlo. El único tipo de proyecto que permitirá múltiples implementaciones parece requerir que las implementaciones sean para iOS o para Android.
fuente
Respuestas:
Tienes una buena pregunta Probablemente haya algunas compensaciones con su solución. La respuesta final realmente depende de lo que quieras decir con dependiente de la plataforma. Por ejemplo, si está iniciando un proceso para iniciar aplicaciones externas y simplemente está cambiando entre una aplicación y otra, probablemente pueda manejarlo sin demasiadas complicaciones. Si está hablando de P / Invoke con bibliotecas nativas, entonces hay un poco más por hacer. Sin embargo, si está vinculando con bibliotecas que solo existen en una plataforma, probablemente necesitará usar múltiples ensamblajes.
Aplicaciones externas
Probablemente no necesite usar
#if
declaraciones en esta situación. Simplemente configure algunas interfaces y tenga una implementación por plataforma. Use una fábrica para detectar la plataforma y proporcionar la instancia correcta.En algunos casos es solo un binario compilado para una plataforma específica, pero el nombre del ejecutable y todos los parámetros se definen de la misma manera. En ese caso, se trata de resolver el ejecutable correcto. Para una aplicación de conversión de audio masiva que podría ejecutarse en Windows y Linux, tuve un inicializador estático que resolvió el nombre binario.
Nada lujoso aquí. Simplemente buenas clases pasadas de moda.
P / Invocar
P / Invoke es un poco más complicado. La conclusión es que debe asegurarse de que se cargue la versión correcta de la biblioteca nativa. En ventanas, P / Invocar
SetDllDirectory()
. Es posible que diferentes plataformas no necesiten ese paso. Así que aquí es donde las cosas pueden complicarse. Es posible que necesite usar#if
declaraciones para controlar qué llamada se usa para controlar la resolución de la ruta de la biblioteca, especialmente si la incluye en su paquete de distribución.Vinculación a bibliotecas dependientes de la plataforma completamente diferentes
El enfoque de orientación múltiple de la vieja escuela puede ser útil aquí. Sin embargo, viene con mucha fealdad. En los días en que algunos proyectos intentaban tener el mismo objetivo DLL Silverlight, WPF y, posiblemente, UAP, tendría que compilar la aplicación varias veces con diferentes etiquetas de compilación. El desafío con cada una de las plataformas anteriores es que, si bien comparten los mismos conceptos, las plataformas son lo suficientemente diferentes como para evitar esas diferencias. Aquí es donde nos metemos en el infierno
#if
.Este enfoque también requiere la edición manual del
.csproj
archivo para manejar referencias dependientes de la plataforma. Dado que su.csproj
archivo es un archivo MSBuild, es completamente posible hacerlo de una manera conocida y predecible.#si el infierno
Puede activar y desactivar secciones de código utilizando
#if
declaraciones para que sea eficaz en el manejo de las pequeñas diferencias entre las aplicaciones. En la superficie parece una buena idea. Incluso lo usé como un medio para activar y desactivar la visualización del cuadro delimitador para depurar el código de dibujo.El problema número 1
#if
es que ninguno de los códigos desactivados es evaluado por el analizador. Es posible que tenga errores de sintaxis latentes o, peor aún, errores lógicos que esperan que vuelva a compilar la biblioteca. Esto se vuelve aún más problemático con la refactorización del código. Algo tan simple como cambiar el nombre de un método o cambiar el orden de los parámetros normalmente se manejaría bien, pero debido a que el analizador nunca evalúa nada desactivado por la#if
declaración, de repente tiene un código roto que no verá hasta que vuelva a compilar.Todo mi código de depuración que se escribió de esa manera tuvo que reescribirse después de que una serie de refactorizaciones lo rompió. Durante la reescritura, utilicé una clase de configuración global para activar y desactivar esas funciones. Eso hizo que fuera una herramienta de prueba refactorizada, pero tal solución no ayuda cuando la API es completamente diferente.
Mi método preferido
Mi método preferido, basado en muchas lecciones dolorosas aprendidas, e incluso basado en el propio ejemplo de Microsoft, es usar múltiples ensamblajes.
Un ensamblaje básico de NetStandard definiría todas las interfaces y contendría todo el código común. Las implementaciones dependientes de la plataforma estarían en un ensamblaje separado que agregaría características cuando se incluyen.
Este enfoque está ejemplificado por la nueva API de configuración y la arquitectura de identidad actual. Como necesita integraciones más específicas, simplemente agregue esos nuevos ensamblajes. Esos conjuntos también proporcionan funciones de extensión para incorporarse a su configuración. Si está utilizando un enfoque de inyección de dependencia, esos métodos de extensión permiten que la biblioteca registre sus servicios.
Esta es la única forma que conozco para evitar el
#if
infierno y satisfacer un entorno sustancialmente diferente.fuente
Microsoft.Extensions.Configuration
conjunto de API.