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