Edição especial do Windows 10 - 2015

Volume 30 - Número 11

Microsoft .NET - .NET e Desenvolvimento para Plataforma Universal do Windows

Por Daniel Jacobson | Windows 2015

Com o Visual Studio 2015, agora você pode usar a mais recente tecnologia do .NET para criar aplicativos da UWP (Plataforma Universal do Windows) que são executados em todos os dispositivos do Windows 10, incluindo o telefone que está em seu bolso, o laptop ou o tablet em sua bolsa, o Surface Hub em seu escritório, o console Xbox em sua casa, o HoloLens e quaisquer outros dispositivos que você possa imaginar na IoT (Internet das Coisas). É um momento realmente empolgante para ser um desenvolvedor do Windows.

Quais são as novidades da UWP?

Como desenvolvedor do .NET, você apreciará tudo o que a UWP tem a oferecer. Os aplicativos da UWP serão executados no modo de "Janela" no enorme número de desktops que foram e continuarão a ser atualizados para o Windows 10. Os aplicativos da UWP poderão abranger todos os dispositivos do Windows 10 com um pacote de aplicativos e uma base de código. Além disso, os aplicativos da UWP aproveitam o novo Microsoft .NET Core Framework (explicado em detalhes mais adiante neste artigo). Sua lógica de negócios do .NET pode ser executada em outras plataformas que oferecem suporte ao .NET Core, como ASP.NET 5. Os aplicativos da UWP implantam uma pequena cópia do .NET Core com o aplicativo, de modo que o aplicativo sempre seja executado em relação à versão do .NET com a qual você o testou. Todos os aplicativos da UWP do .NET aproveitam plenamente o .NET Nativo que gera código de máquina nativo altamente otimizado, resultando em ganhos de desempenho (o que também é explicado neste artigo).

.NET Core Framework

O .NET Framework Core é uma nova versão do .NET para cargas de trabalho modernas de dispositivos e de nuvem. É uma implementação modular e de uso geral do Microsoft .NET Framework que pode ser transportada e usada em muitos ambientes diferentes para diversas cargas de trabalho. Além disso, o .NET Framework Core é um software de código aberto que está disponível no GitHub (github.com/dotnet/corefx) e tem suporte da Microsoft no Windows, Linux e Mac OS X. Se você é um desenvolvedor da UWP que está usando a mais recente tecnologia .NET, isso lhe oferece enormes vantagens. No Visual Studio 2015, você pode utilizar PCLs (bibliotecas de classes portáteis) do .NET Core para abranger qualquer aplicativo da UWP, do .NET 4.6 ou do ASP.NET 5, mesmo aqueles que são de plataforma cruzada.

Além disso, o .NET Core Framework é um superconjunto de APIs .NET que estavam disponíveis anteriormente no desenvolvimento de aplicativos da Windows Store. Isso significa que os desenvolvedores da UWP agora têm vários namespaces adicionais disponíveis em seu arsenal de APIs. Um desses namespaces é System.Net.Sockets, que é usado para na comunicação de UDP. Anteriormente, isso estava indisponível em aplicativos do WinRT (Tempo de Execução do Windows) e a solução alternativa era usar as APIs UDP específicas do WinRT. Agora que o Sockets está disponível no .NET Core, você pode usar o mesmo código de Soquete em seus aplicativos da UWP e em outros aplicativos .NET.

Outra vantagem é que a API System.Net.Http.HttpClient se baseia nas pilhas HTTP do WinRT. Isso fornece a capacidade de usar HTTP/2 por padrão, se o servidor prestar suporte a ele, resultando em menor latência e em menos comunicações de ida e volta.

O cliente WCF (Windows Communication Foundation) (e a caixa de diálogo Adicionar Referência de Serviço associada) anteriormente estava indisponível em projetos .appx do Windows Phone, mas como ele faz parte do .NET Core, pode ser usado por todos os aplicativos da UWP do .NET.

Finalmente, o .NET Core é a estrutura subjacente da qual o .NET Nativo depende. Quando o .NET Nativo foi projetado, ficou claro que o .NET Framework não seria adequado para servir de base para as bibliotecas de classe de estrutura. Isso ocorre porque o .NET Nativo vincula estaticamente a estrutura ao aplicativo e, em seguida, remove os itens adicionais que não são necessários para o aplicativo. (Essa é uma explicação muito simplificada, serve mais para oferecer uma noção geral. Para obter mais detalhes, consulte "Dentro do .NET Nativo" em bit.ly/1UR7ChW.)

A implementação tradicional do .NET Framework não é fatorada, assim, é um desafio para que um vinculador reduza a quantidade de estrutura que é compilada no aplicativo. Afinal, o .NET Core é, essencialmente, uma bifurcação do .NET Framework, cuja implementação é otimizada em torno das preocupações de fatoração. Outro benefício dessa implementação é a capacidade de fornecer o .NET Core Framework como um conjunto de pacotes NuGet para que você possa atualizar classes individuais fora de banda do .NET Framework. Porém, antes de prosseguir, vamos falar sobre as alterações no NuGet.

Quais são as novidades no NuGet?

Com a UWP, o suporte ao NuGet 3.1 é interno. Neste lançamento estão incluídos recursos que melhoram o gerenciamento de dependência de pacote e um cache local de seus pacotes para reutilização em vários projetos.

Com o NuGet 3.1, o modelo de declaração de dependência de pacote foi atualizado. A partir do ASP.NET 5, o NuGet introduziu o suporte ao arquivo project.json e esse é o mesmo modelo ao qual a UWP presta suporte. O Project.json permite descrever as dependências de um projeto com uma definição clara dos pacotes dos quais você depende imediatamente. Como o formato é o mesmo para o ASP.NET 5 e a UWP, você pode usar o mesmo arquivo para definir referências de pacotes para ambas as plataformas, bem como PCLs.

Mudar de packages.config para project.json permite "reinicializar" as referências em seus projetos e já existe um novo recurso de dependência transitiva do NuGet. Se você referencia um pacote que referencia outro pacote do NuGet, costumava ser difícil gerenciar o controle de versão de pacotes. Por exemplo, o NHibernate é um pacote que depende de lesi.Collections. Em packages.config, há duas referências, uma para cada pacote. Quando quer atualizar NHibernate, você também atualiza lesi.collections? Ou então, se há uma atualização para lesi.collections, você também tem que atualizar NHibernate para dar suporte aos novos recursos? Isso se tornou um ciclo complicado e o gerenciamento de versão de pacotes era difícil. O recurso de dependências transitivas do NuGet abstrai essa decisão para atualizar referências a pacotes, com controle de versão semântico melhorado nos arquivos de definição de pacote (nuspecs).

Além disso, o NuGet agora baixa e armazena uma cópia dos pacotes que você usa em uma pasta de pacotes globais localizada na pasta %userprofile%\.nuget\packages. Isso melhorará o desempenho nas referências a pacotes porque você só precisará baixar cada pacote uma vez. Além disso, deverá reduzir o espaço em disco usado na estação de trabalho por poder compartilhar os mesmos binários de pacote de um projeto para outro.

NuGet e .NET Core

O que acontece quando você combina o .NET Core voltado para fatoração ao gerenciamento de dependências de pacotes do NuGet 3.1? Você obtém a capacidade de atualizar pacotes individuais do .NET Framework fora de banda do restante do .NET Framework. Com a UWP, o .NET Core é incluído como um conjunto de pacotes NuGet em seu aplicativo. Ao criar um novo projeto, você verá somente a dependência de pacote geral Microsoft.NETCore.UniversalWindowsPlatform, mas, se examinar esse pacote no NuGet, verá todas as bibliotecas do .NET Framework que estão incluídas, conforme mostrado na Figura 1.

Exibindo Bibliotecas do .NET Framework no NuGet
Figura 1 Exibindo Bibliotecas do .NET Framework no NuGet

Por exemplo, digamos que haja uma atualização para a classe System.Net.Sockets que introduz uma nova API que você deseja usar em seu aplicativo. Com o .NET tradicional, o aplicativo precisa aplicar uma dependência em uma nova compilação de todo o .NET Framework. Com a UWP e o .NET Core com NuGet, você pode atualizar suas dependências do NuGet para incluir a última versão apenas desse pacote. Então, quando o aplicativo é compilado e empacotado, essa versão da biblioteca da estrutura será incluída no aplicativo. Isso oferece-lhe a flexibilidade para usar a melhor e mais recente tecnologia .NET, sem forçar os usuários a sempre ter a estrutura mais recente instalada em seus dispositivos.

Além de poder atualizar suas classes .NET em seu próprio ritmo, a divisão em componentes do .NET Core também habilita o .NET Nativo, proporcionando benefícios ao desempenho de todos os aplicativos do Visual Basic e C# do Windows 10 quando fornecidos a dispositivos de consumo.

O que é o .NET Nativo?

Agora que você sabe que o .NET Framework Core habilita o .NET Nativo, vou explicar mais detalhadamente o que ele é e o que faz para você como desenvolvedor da UWP.

O .NET Nativo é um processo de compilação AOT (antecipado) que transforma seu código .NET gerenciado em código de máquina nativo em tempo de compilação. Em contraste, o .NET tradicional usa a compilação JIT (just-in-time) que adia a compilação nativa de um método até sua primeira execução em tempo de execução. O .NET Nativo é mais semelhante a um compilador de C++. Na verdade, ele usa o compilador de C++ do Visual Studio como parte de sua cadeia de ferramentas. Todos os aplicativos Universal do Windows gerenciados (C# ou Visual Basic) usarão essa nova tecnologia. Os aplicativos são automaticamente compilados para o código nativo antes de chegarem aos dispositivos dos clientes. Se quiser entender mais a fundo como isso funciona, recomendo ler o artigo da Biblioteca do MSDN "Compilando Aplicativos com o .NET Nativo", em bit.ly/1QcTGxm.

Como o .NET Nativo Afeta Você e seu Aplicativo?

Sua experiência provavelmente variará, mas, na maioria dos casos, o aplicativo será inicializado mais depressa, terá melhor desempenho e consumirá menos recursos do sistema. Você pode esperar uma melhoria de desempenho de até 60% em seus aplicativos quando eles forem inicializados pela primeira vez e uma melhoria de até 40% nos tempos de inicialização subsequentes (inicialização "a quente"). Seus aplicativos consumirão menos memória quando compilados de forma nativa. Todas as dependências do tempo de execução do .NET são removidas, assim, os usuários finais nunca precisarão sair de sua experiência de configuração para adquirir a versão específica do .NET Framework que seu aplicativo referencia. De fato, todas as dependências do .NET são empacotadas no aplicativo, assim, o comportamento do aplicativo não deve mudar só porque há uma alteração no .NET Framework instalado no computador.

Embora o aplicativo esteja sendo compilado para binários nativos, você ainda pode aproveitar as linguagens do .NET com as quais está familiarizado (C# ou Visual Basic) e as excelentes ferramentas associadas a elas. Finalmente, você pode continuar a usar o modelo de programação abrangente e consistente disponível com o .NET Framework, com APIs maiores para a lógica de negócios, gerenciamento interno de memória e tratamento de exceções.

Com o .NET Nativo, você obtém o melhor de dois mundos: gerenciamento de desenvolvimento e desempenho do C++. O quão interessante é isso?

Configuração de Depuração Versus Lançamento e Compilação

A compilação do .NET Nativo é um processo complexo tornando-o um pouco mais lento do que a compilação do .NET clássico. Os benefícios mencionados anteriormente têm a desvantagem do tempo de compilação. Você pode optar por compilar de forma nativa sempre que desejar executar o aplicativo, mas gastaria mais tempo aguardando o término da compilação. O conjunto de ferramentas do Visual Studio é projetado para resolver isso e criar a experiência de desenvolvedor mais perfeita possível.

Ao compilar e executar na configuração de "Depuração", você executa o código de Linguagem Intermediária em relação ao CoreCLR empacotado em seu aplicativo. Os assemblies do sistema .NET são empacotados juntamente com o código do aplicativo e o aplicativo tem uma dependência em relação ao pacote Microsoft.NET.CoreRuntime (CoreCLR). Se a estrutura CoreCLR estiver ausente no dispositivo em que você está testando, o Visual Studio detectará isso automaticamente e o instalará antes de implantar o aplicativo.

Isso significa que você obtém a melhor experiência de desenvolvimento possível: compilação e implantação rápidas, depuração e diagnósticos avançados e todas as ferramentas às quais você está acostumado com o desenvolvimento no .NET.

Quando você alterna para o modo de "Lançamento", por padrão, o aplicativo utiliza o conjunto de ferramentas do .NET Nativo. Como o pacote é compilado para binários nativos, ele não precisa conter as bibliotecas do .NET Framework. Além disso, o pacote depende do último tempo de execução do .NET Nativo instalado, em vez do pacote do CoreCLR. O tempo de execução do .NET Nativo no dispositivo será sempre compatível com seu pacote de aplicativos.

A compilação nativa local por meio da configuração de "Lançamento" habilitará o teste do aplicativo em um ambiente que é semelhante ao que seus clientes experimentarão. É importante testar isso regularmente ao prosseguir com o desenvolvimento! Ao testar o aplicativo usando a tecnologia de geração de código e tempo de execução que seus clientes experimentarão, você garante que abordou todos os bugs possíveis (como condições de corrida potenciais que resultarão de diferentes características de desempenho).

Uma boa estratégia consiste em testar o aplicativo dessa maneira periodicamente ao longo de todo o desenvolvimento para garantir a identificação e a correção de quaisquer problemas decorrentes do compilador do .NET Nativo. Não deve haver problemas na maioria dos casos. No entanto, ainda existem algumas coisas que não funcionam tão bem no .NET Nativo. Quatro ou mais matrizes dimensionais são um exemplo disso. Em última análise, os clientes obterão a versão compilada do .NET Nativo de seu aplicativo. Por isso, é sempre uma boa ideia testar essa versão ao longo de todo o desenvolvimento e antes do envio.

Além de testar com a compilação do .NET Nativo, você também pode observar que a configuração de compilação AnyCPU desapareceu. Com o .NET Nativo, a AnyCPU não é mais uma configuração de compilação válida porque a compilação nativa depende da arquitetura. Uma consequência adicional disso é que, ao empacotar o aplicativo, você deve selecionar todas as três configurações de arquitetura (x86, x64 e ARM) para garantir que o aplicativo seja aplicável ao maior número de dispositivos possível. Afinal de contas, essa é a Plataforma Universal do Windows.

Dito isso, você ainda pode compilar DLLs e bibliotecas AnyCPU para serem referenciadas em seu aplicativo da UWP. Esses componentes serão compilados para binários específicos de arquiteturas com base na configuração do projeto (.appx) que os consume.

.NET Nativo na Nuvem

Um ótimo recurso do .NET Nativo é que o compilador pode ser hospedado na nuvem. Isso significa que quando são feitas melhorias no compilador que podem ter um impacto benéfico sobre seu aplicativo, o compilador do .NET Nativo hospedado na nuvem da Loja pode recompilar seu pacote de aplicativos para se beneficiar. Sempre que essa compilação for feita, ela será transparente para você como desenvolvedor, mas, em última análise, significa que os consumidores de seu aplicativo ficarão mais satisfeitos.

No entanto, isso pode ter algum impacto sobre seu fluxo de trabalho. Por exemplo, é uma boa ideia garantir que você sempre tenha as ferramentas mais recentes instaladas para que possa testar sua compilação do .NET Nativo em relação à versão local mais recente do compilador. Além disso, quando você compila seu pacote da Loja no Visual Studio, são criados dois pacotes: um .appxupload e um .appx de "teste" para sideload. O .appxupload contém os binários MSIL, além de uma referência explícita à versão da cadeia de ferramentas do .NET Nativo que seu aplicativo consome (referenciada no AppxManifest.xml como "ilc.exe"). Este pacote é então enviado à Loja e é compilado usando exatamente a mesma versão que a cadeia de ferramentas do .NET Nativo. Como o compilador é hospedado na nuvem, ele pode iterar para corrigir bugs sem que você tenha que recompilar seu aplicativo localmente.

Com o .NET Nativo, você precisa ter cuidado em relação a qual pacote carregar para a Loja. Como a Loja faz a compilação nativa para você, você não pode carregar os binários nativos gerados pelo compilador do .NET Nativo local. O fluxo de trabalho do Visual Studio vai guiá-lo ao longo desse processo para que você selecione o pacote certo. Para obter diretrizes completas sobre como criar um pacote da Loja, consulte o artigo da Biblioteca do MSDN "Empacotando Aplicativos Universais do Windows para o Windows 10", em bit.ly/1OQTTG0. Isso vai guiá-lo ao longo do processo de criação do pacote, garantindo que você gere e escolha o pacote certo para carregar na Loja.

Depurando com o .NET Nativo

Se você encontrar problemas em seu aplicativo que você suspeite que sejam causados pelo .NET Nativo, há uma técnica que pode ser usada para ajudar a depurar o problema. As configurações de lançamento otimizam totalmente o código (por exemplo, o código inlining é aplicado em muitos lugares), por padrão, o que elimina alguns artefatos de depuração. Como resultado, pode ser difícil tentar depurar um aplicativo de configuração de Lançamento. Podem ocorrer etapas imprevisíveis e comportamento de ponto de interrupção, além da incapacidade de inspecionar variáveis causada pela otimização de memória. Como o comportamento padrão de configurações de Lançamento é usar o compilador do .NET Nativo com otimização de código, é difícil depurar quaisquer problemas que possam ser decorrentes do processo de compilação do .NET Nativo.

Uma boa maneira de contornar esse problema é criar uma configuração de compilação personalizada para seu projeto que utilize o compilador do .NET Nativo, mas não otimize totalmente o código. Para criar uma configuração de compilação personalizada, abra o Configuration Manager na lista suspensa de configuração de compilação, como mostra a Figura 2.

Abrindo o Configuration Manager
Figura 2 Abrindo o Configuration Manager

Na lista suspensa de Configuração de solução Ativa, escolha <Novo...> para criar uma nova configuração, conforme mostrado na Figura 3.

Criando uma Nova Configuração
Figura 3 Criando uma Nova Configuração

Dê à nova configuração um nome que será útil para você mais tarde. Gosto de usar "Depurar .NET Nativo". Copie as configurações da configuração de compilação de "Lançamento" e clique em OK.

Feche o Configuration Manager e abra a página de propriedades do projeto clicando com o botão direito do mouse no projeto no Gerenciador de Soluções e clicando em Propriedades. Navegue até a guia Compilar e verifique se a opção Compilar com cadeia de ferramentas .NET Nativo está marcada e a opção Otimizar código está desmarcada, como mostrado na Figura 4.

Criando uma Configuração de Compilação para Depuração do .NET Nativo
Figura 4 Criando uma Configuração de Compilação para Depuração do .NET Nativo

Agora você tem uma configuração de compilação que pode ser usada para depurar problemas específicos do .NET Nativo.

Para obter mais informações sobre depuração com o .NET Nativo, consulte o artigo da Biblioteca do MSDN "Depurando Aplicativos Universais do Windows do .NET Nativo", em bit.ly/1Ixd07v.

Analisador do .NET Nativo

É claro que é bom saber como depurar problemas, mas não seria melhor se você pudesse evitá-los desde o início? The Microsoft.NETNative.Analyzer (bit.ly/1LugGnO) pode ser instalado em seu aplicativo via NuGet. No Console do Gerenciador de Pacotes, você pode instalar o pacote com o seguinte comando: Install-Package Microsoft.NETNative.Analyzer. No período de desenvolvimento, esse analisador emitirá avisos se o código não for compatível com o compilador do .NET Nativo. Há uma pequena parte do .NET que não é compatível, mas, para a maioria dos aplicativos, isso nunca será um problema.

Considerações finais

Como você pode ver, é um momento empolgante para ser um desenvolvedor do Windows do .NET. Com a UWP, o .NET Nativo e as alterações no NuGet, nunca foi tão fácil criar aplicativos para tantos dispositivos diferentes que seus clientes vão adorar. Pela primeira vez, você pode aproveitar os mais recentes avanços em qualquer classe do .NET e ainda esperar que seu aplicativo seja executado em todos os dispositivos do Windows 10.


Daniel Jacobsoné um gerente de programa do Visual Studio que trabalha com ferramentas para desenvolvedores da plataforma Windows. Entre em contato com ele pelo email dajaco@microsoft.com.

Agradecemos aos seguintes especialistas técnicos pela revisão deste artigo: Kino Aguilar, Adam Denning, Yishai Galatzer, Jenny Hayes, Jeremy Meng, Harikrishna Menon, Jessica Prince, Unni Ravindranathan, Navit Saxena, Michael Strehovsky, Debbie Thorn, Matthew Whilde, Lucian Wischik