Estamos tentando desenvolver um aplicativo de plataforma cruzada para desktops (Windows e Mac) usando C #. O aplicativo contém uma quantidade enorme de coisas dependentes da plataforma e nosso líder de equipe deseja que escrevamos todo esse código em C #. A maneira mais fácil de fazer isso seria escrever wrappers em C ++ e apenas fazer referência às bibliotecas no código C # e deixar que as bibliotecas C ++ lidassem com o material da plataforma ... infelizmente, isso não deve acontecer.
Estou tentando configurar uma biblioteca usando o Visual Studio para Mac. Espero poder fornecer uma única interface em uma única biblioteca para conceder acesso à biblioteca de texto nativo para fala na plataforma atual - de preferência sem fazer dois assemblies. A biblioteca de conversão de texto em fala em C # para Windows não está disponível no Mac; portanto, não temos realmente uma opção alternativa do que fornecer uma ponte.
Estou feliz em ter a biblioteca pesquisando o sistema operacional para determinar em qual sistema operacional está sendo executado e / ou tentando carregar um monte de bibliotecas nativas e apenas usando qual biblioteca será carregada.
Se eu precisar, ter um único projeto que me permita escrever duas implementações terá que fazer. Não encontrei uma maneira de criar um projeto de biblioteca no Visual Studio para Mac, o que me permitirá fazê-lo. O único tipo de projeto que permitirá várias implementações parece exigir que as implementações sejam para iOS ou Android.
fonte
Respostas:
Você tem uma boa pergunta. Provavelmente existem algumas vantagens e desvantagens com sua solução. A resposta final realmente depende do que você quer dizer com dependente da plataforma. Por exemplo, se você está iniciando um processo para iniciar aplicativos externos e está simplesmente alternando entre um aplicativo e outro, provavelmente poderá lidar com isso sem muita complicação. Se você está falando de P / Invoke com bibliotecas nativas, há um pouco mais a ser feito. No entanto, se você estiver vinculando com bibliotecas que existem apenas em uma plataforma, provavelmente precisará usar vários assemblies.
Aplicativos externos
Você provavelmente não precisará usar
#if
instruções nessa situação. Basta configurar algumas interfaces e ter uma implementação por plataforma. Use uma fábrica para detectar a plataforma e fornecer a instância correta.Em alguns casos, é apenas um binário compilado para uma plataforma específica, mas o nome do executável e todos os parâmetros são definidos da mesma forma. Nesse caso, é uma questão de resolver o executável correto. Para um aplicativo conversor de áudio em massa que poderia ser executado no Windows e Linux, solicitei que um inicializador estático resolvesse o nome binário.
Nada extravagante aqui. Apenas boas aulas antigas.
P / Invoke
P / Invoke é um pouco mais complicado. A linha inferior é que você precisa garantir que a versão correta da biblioteca nativa seja carregada. Nas janelas, você faria P / Invoke
SetDllDirectory()
. Plataformas diferentes podem não precisar dessa etapa. Então é aqui que as coisas podem ficar confusas. Pode ser necessário usar#if
instruções para controlar qual chamada é usada para controlar a resolução do caminho da biblioteca - principalmente se você a incluir no pacote de distribuição.Vinculando a Bibliotecas Dependentes da Plataforma completamente Diferentes
A abordagem de segmentação antiga à moda antiga pode ser útil aqui. No entanto, ele vem com muita feiura. Nos dias em que alguns projetos tentavam ter o mesmo destino de DLL do Silverlight, WPF e potencialmente UAP, seria necessário compilar o aplicativo várias vezes com diferentes marcas de compilação. O desafio de cada uma das plataformas acima é que, embora elas compartilhem os mesmos conceitos, as plataformas são suficientemente diferentes para que você precise solucionar essas diferenças. É aqui que entramos no inferno
#if
.Essa abordagem também requer edição manual do
.csproj
arquivo para lidar com referências dependentes da plataforma. Como seu.csproj
arquivo é um arquivo MSBuild, é totalmente possível fazer isso de maneira conhecida e previsível.#se inferno
Você pode ativar e desativar seções de código usando
#if
instruções para que seja eficaz no tratamento de pequenas diferenças entre os aplicativos. Na superfície, parece uma boa ideia. Eu até o usei como um meio de ativar e desativar a visualização da caixa delimitadora para depurar o código de desenho.O problema número 1
#if
é que nenhum código desativado é avaliado pelo analisador. Você pode ter erros de sintaxe latentes ou, pior, erros de lógica esperando que você recompile a biblioteca. Isso se torna ainda mais problemático com o código de refatoração. Algo tão simples como renomear um método ou alterar a ordem dos parâmetros normalmente seria tratado como OK, mas como o analisador nunca avalia nada desativado pela#if
instrução, você repentinamente quebrou o código que não verá até recompilar.Todo o meu código de depuração que foi escrito dessa maneira teve que ser reescrito depois que uma série de refatorações o quebrou. Durante a reescrita, usei uma classe de configuração global para ativar e desativar esses recursos. Isso fez com que refatorasse a prova de ferramentas, mas essa solução não ajuda quando a API é completamente diferente.
Meu método preferido
Meu método preferido, com base em muitas lições dolorosas aprendidas, e mesmo com base no próprio exemplo da Microsoft, é usar várias montagens.
Um assembly NetStandard principal definiria todas as interfaces e conteria todo o código comum. As implementações dependentes da plataforma estariam em um assembly separado que adicionaria recursos quando incluídos.
Essa abordagem é exemplificada pela nova API de configuração e pela arquitetura de identidade atual. Como você precisa de integrações mais específicas, basta adicionar esses novos assemblies. Esses conjuntos também fornecem funções de extensão para incorporar-se à sua configuração. Se você estiver usando uma abordagem de injeção de dependência, esses métodos de extensão permitirão que a biblioteca registre seus serviços.
Essa é a única maneira que conheço de evitar o
#if
inferno e satisfazer um ambiente substancialmente diferente.fonte
Microsoft.Extensions.Configuration
conjunto de APIs.