¿Hay alguna manera de hacer una biblioteca .Net para Windows y Mac, con referencias dependientes de la plataforma?


12

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.


¿Qué tiempo de ejecución de .NET vas a utilizar?
Eufórico

2
Xamarin hace algo similar. ¿Quizás con configuraciones de compilación?
Ewan

2
Vale la pena señalar que Visual Studio para mac no es realmente un estudio visual, es solo un estudio de Xamarin modificado. ¿Quizás hay algo en los documentos de Xamarin?
richzilla

3
El mejor enfoque serán diferentes ensamblajes ... Uno para las interfaces y el código común y uno para la plataforma de destino. Sin embargo, puede usar múltiples objetivos y usar declaraciones #if para intercambiar implementaciones. La desventaja es que todo lo que no está habilitado no se evalúa, por lo que puede quedar desactualizado fácilmente.
Berin Loritsch

Cuando dice referencias dependientes de la plataforma, ¿habla código nativo o todo el código C #?
Berin Loritsch

Respuestas:


4

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 #ifdeclaraciones 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.

public class AudioProcessor
{
    private static readonly string AppName = "lame";
    private static readonly string FullAppPath;

    static AudioProcessor()
    {
        var platform = DetectPlatform();
        var architecture = Detect64or32Bits();

        FullAppPath = Path.combine(platform, architecture, AppName);
    }
}

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 #ifdeclaraciones 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 .csprojarchivo para manejar referencias dependientes de la plataforma. Dado que su .csprojarchivo 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 #ifdeclaraciones 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 #ifes 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 #ifdeclaració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 #ifinfierno y satisfacer un entorno sustancialmente diferente.


Soy un asco en C # (es normal que haya usado C ++ durante aproximadamente 18 años y C # durante aproximadamente medio día). Esta nueva API de configuración ... ¿podría proporcionar algunos enlaces a eso? Parece que no puedo encontrarlo por mi cuenta.
Más claro

2
docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/… Hay múltiples paquetes Nuget para agregar fuentes de configuración adicionales. Por ejemplo, de variables de entorno, archivos JSON, etc.
Berin Loritsch

1
Es el Microsoft.Extensions.Configurationconjunto de API.
Berin Loritsch

1
Aquí está el proyecto raíz de github: github.com/aspnet/Configuration
Berin Loritsch
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.