Partilhar via


Migrar seu aplicativo do Windows 8.x para o .NET Native

O .NET Native fornece compilação estática de aplicativos na Microsoft Store ou no computador do desenvolvedor. Isso difere da compilação dinâmica realizada para aplicativos do Windows 8.x (também chamados anteriormente de aplicativos da Microsoft Store) pelo compilador just-in-time (JIT) ou pelo Native Image Generator (Ngen.exe) no dispositivo. Apesar das diferenças, o .NET Native tenta manter a compatibilidade com o .NET para aplicativos do Windows 8.x. Na maioria das vezes, as coisas que funcionam no .NET para aplicativos do Windows 8.x também funcionam com o .NET Native. No entanto, em alguns casos, você pode encontrar mudanças comportamentais. Este documento discute essas diferenças entre o .NET padrão para aplicativos do Windows 8.x e o .NET Native nas seguintes áreas:

Diferenças gerais de tempo de execução

  • Exceções, como TypeLoadException, que são lançadas pelo compilador JIT quando um aplicativo é executado no Common Language Runtime (CLR) geralmente resultam em erros em tempo de compilação quando processados pelo .NET Native.

  • Não chame o GC.WaitForPendingFinalizers método a partir do thread da interface do usuário de um aplicativo. Isso pode resultar em um impasse no .NET Native.

  • Não confie na ordem de invocação do construtor de classe estática. No .NET Native, a ordem de invocação é diferente da ordem no tempo de execução padrão. (Mesmo com o tempo de execução padrão, você não deve confiar na ordem de execução dos construtores de classe estática.)

  • Looping infinito sem fazer uma chamada (por exemplo, while(true);) em qualquer thread pode fazer com que o aplicativo pare. Da mesma forma, esperas grandes ou infinitas podem paralisar o aplicativo.

  • Certos ciclos de inicialização genéricos não geram exceções no .NET Native. Por exemplo, o código a seguir lança uma TypeLoadException exceção no CLR padrão. No .NET Native, isso não acontece.

    using System;
    
    struct N<T> {}
    struct X { N<X> x; }
    
    public class Example
    {
       public static void Main()
       {
          N<int> n = new N<int>();
          X x = new X();
       }
    }
    
  • Em alguns casos, o .NET Native fornece diferentes implementações de bibliotecas de classes do .NET Framework. Um objeto retornado de um método sempre implementará os membros do tipo retornado. No entanto, como a sua implementação de base é diferente, talvez não se consiga lançá-lo para o mesmo conjunto de tipos como se pode em outras plataformas do .NET Framework. Por exemplo, em alguns casos, talvez não seja possível converter o IEnumerable<T> objeto de interface retornado por métodos como TypeInfo.DeclaredMembers ou TypeInfo.DeclaredProperties para T[].

  • O cache WinInet não está habilitado por padrão no .NET para aplicativos do Windows 8.x, mas está no .NET Native. Isso melhora o desempenho, mas tem implicações no conjunto de trabalho. Nenhuma ação do desenvolvedor é necessária.

Diferenças de programação dinâmica

O .NET Native vincula estaticamente o código do .NET Framework para torná-lo local à aplicação e obter o máximo de desempenho. No entanto, os tamanhos binários devem permanecer pequenos, portanto, todo o .NET Framework não pode ser trazido. O compilador .NET Native resolve essa limitação usando um redutor de dependência que remove referências a código não utilizado. No entanto, o .NET Native pode não manter ou gerar algumas informações de tipo e código quando essas informações não podem ser inferidas estaticamente em tempo de compilação, mas são recuperadas dinamicamente em tempo de execução.

O .NET Native permite a reflexão e a programação dinâmica. No entanto, nem todos os tipos podem ser marcados para reflexão, porque isso tornaria o tamanho do código gerado muito grande (especialmente porque a reflexão sobre APIs públicas no .NET Framework é suportada). O compilador .NET Native faz escolhas inteligentes sobre quais tipos devem suportar reflexão, e mantém os metadados e gera código apenas para esses tipos.

Por exemplo, a vinculação de dados requer que um aplicativo seja capaz de mapear nomes de propriedade para funções. No .NET para aplicativos Windows 8.x, o Common Language Runtime usa automaticamente a reflexão para fornecer esse recurso para tipos gerenciados e tipos nativos disponíveis publicamente. No .NET Native, o compilador inclui automaticamente metadados para tipos aos quais você vincula dados.

O compilador .NET Native também pode lidar com tipos genéricos comumente usados, como List<T> e Dictionary<TKey,TValue>, que funcionam sem exigir dicas ou diretivas. A palavra-chave dynamic também é suportada dentro de certos limites.

Observação

Você deve testar todos os caminhos de código dinâmico completamente ao portar seu aplicativo para o .NET Native.

A configuração padrão para o .NET Native é suficiente para a maioria dos desenvolvedores, mas alguns desenvolvedores podem querer ajustar suas configurações usando um arquivo de diretivas de tempo de execução (.rd.xml). Além disso, em alguns casos, o compilador .NET Native não consegue determinar quais metadados devem estar disponíveis para reflexão e depende de dicas, particularmente nos seguintes casos:

  • Algumas construções, como Type.MakeGenericType e MethodInfo.MakeGenericMethod, não podem ser determinadas estaticamente.

  • Como o compilador não pode determinar as instanciações, um tipo genérico sobre o qual você deseja refletir deve ser especificado por diretivas de tempo de execução. Isso não ocorre apenas porque todo o código deve ser incluído, mas porque a reflexão sobre tipos genéricos pode formar um ciclo infinito (por exemplo, quando um método genérico é invocado em um tipo genérico).

Observação

As diretivas de tempo de execução são definidas em um arquivo de diretivas de tempo de execução (.rd.xml). Para obter informações gerais sobre como usar esse arquivo, consulte Introdução. Para obter informações sobre as diretivas de tempo de execução, consulte Diretivas de Tempo de Execução (rd.xml) Referência do Ficheiro de Configuração.

O .NET Native também inclui ferramentas de criação de perfil que ajudam o desenvolvedor a determinar quais tipos fora do conjunto padrão devem oferecer suporte à reflexão.

Há uma série de outras diferenças individuais relacionadas à reflexão no comportamento entre o .NET para aplicativos do Windows 8.x e o .NET Native.

No .NET Nativo:

  • Não há suporte para reflexão privada sobre tipos e membros na biblioteca de classes do .NET Framework. No entanto, você pode refletir sobre seus próprios tipos e membros privados, bem como tipos e membros em bibliotecas de terceiros.

  • A ParameterInfo.HasDefaultValue propriedade retorna false corretamente para um ParameterInfo objeto que representa um valor de retorno. No .NET para aplicativos do Windows 8.x, ele retorna true. A linguagem intermediária (IL) não suporta isso diretamente, e a interpretação é deixada para o idioma.

  • Os membros públicos das estruturas RuntimeFieldHandle e RuntimeMethodHandle não são apoiados. Esses tipos são suportados apenas para LINQ, árvores de expressão e inicialização de matriz estática.

  • RuntimeReflectionExtensions.GetRuntimeProperties e RuntimeReflectionExtensions.GetRuntimeEvents incluem os membros ocultos em classes base e, assim, podem ser substituídos sem necessidade de substituições explícitas. Isso também é verdade para outros métodos dos RuntimeReflectionExtensions.GetRuntime*.

  • Type.MakeArrayType e Type.MakeByRefType não falham quando tenta criar certas combinações (por exemplo, uma matriz de objetos byref).

  • Não é possível usar a reflexão para invocar membros que tenham parâmetros de ponteiro.

  • Não é possível usar o reflexo para obter ou definir um campo de ponteiro.

  • Quando a contagem de argumentos está errada e o tipo de um dos argumentos está incorreto, o .NET Native lança um ArgumentException em vez de um TargetParameterCountException.

  • A serialização binária de exceções geralmente não é suportada. Como resultado, objetos não serializáveis podem ser adicionados ao Exception.Data dicionário.

Cenários e APIs sem suporte

As seções a seguir listam cenários e APIs sem suporte para desenvolvimento geral, interoperabilidade e tecnologias, como HTTPClient e Windows Communication Foundation (WCF):

Diferenças gerais de desenvolvimento

Tipos de valor

  • Se você substituir os métodos ValueType.Equals e ValueType.GetHashCode para um tipo de valor, não chame as implementações de classe base. No .NET para aplicativos Windows 8.x, esses métodos dependem da reflexão. Em tempo de compilação, o .NET Native gera uma implementação que não depende da reflexão em tempo de execução. Isso significa que, se você não substituir esses dois métodos, eles funcionarão conforme o esperado, porque o .NET Native gera a implementação em tempo de compilação. No entanto, ao substituir estes métodos e chamar a implementação da classe base, ocorre uma exceção.

  • Não há suporte para tipos de valor maiores que 1 megabyte.

  • Os tipos de valor não podem ter um construtor sem parâmetros no .NET Native. (C# e Visual Basic proíbem construtores sem parâmetros em tipos de valor. No entanto, estes podem ser criados em IL.)

Matrizes

  • Não há suporte para matrizes com um limite inferior diferente de zero. Normalmente, essas matrizes são criadas chamando o Array.CreateInstance(Type, Int32[], Int32[]) sobrecarga.

  • A criação dinâmica de matrizes multidimensionais não é suportada. Essas matrizes são normalmente criadas chamando uma sobrecarga do método Array.CreateInstance que inclui um parâmetro lengths, ou chamando o método Type.MakeArrayType(Int32).

  • Não há suporte para matrizes multidimensionais com quatro ou mais dimensões; ou seja, o valor da sua propriedade Array.Rank é igual ou superior a quatro. Em vez disso, use matrizes irregulares (uma matriz de matrizes). Por exemplo, array[x,y,z] é inválido, mas array[x][y][z] não é.

  • A variância para matrizes multidimensionais não é suportada e causa uma InvalidCastException exceção em tempo de execução.

Genéricos

  • A expansão infinita do tipo genérico resulta em um erro do compilador. Por exemplo, este código não consegue compilar:

    class A<T> {}
    
    class B<T> : A<B<A<T>>>
    {}
    

Ponteiros

  • Não há suporte para matrizes de ponteiros.

  • Não é possível usar o reflexo para obter ou definir um campo de ponteiro.

Serialização

O KnownTypeAttribute(String) atributo não é suportado. Em vez disso, use o KnownTypeAttribute(Type) atributo.

Recursos

Não há suporte para o uso de recursos localizados com a EventSource classe. A EventSourceAttribute.LocalizationResources propriedade não define recursos localizados.

Delegados

Delegate.BeginInvoke e Delegate.EndInvoke não são suportados.

APIs diversas

  • A propriedade TypeInfo.GUID lança uma PlatformNotSupportedException exceção se um GuidAttribute atributo não for aplicado ao tipo. O GUID é usado principalmente para suporte COM.

  • O DateTime.Parse método analisa corretamente cadeias de caracteres que contêm datas curtas no .NET Native. No entanto, ele não mantém a compatibilidade com certas alterações na análise de data e hora.

  • BigInteger.ToString ("E") está corretamente arredondado no .NET Native. Em algumas versões do CLR, a cadeia de caracteres de resultado é truncada em vez de arredondada.

Diferenças de HttpClient

No .NET Native, a classe HttpClientHandler usa internamente o WinINet (através da classe HttpBaseProtocolFilter) em vez de usar as classes WebRequest e WebResponse utilizadas no .NET padrão para aplicativos do Windows 8.x. WinINet não suporta todas as opções de configuração que a HttpClientHandler classe suporta. Como resultado:

  • Algumas das propriedades de capacidade em HttpClientHandler retornam false no .NET Native, enquanto retornam true no .NET padrão para aplicativos do Windows 8.x.

  • Alguns dos acessores das propriedades de configuração get retornam sempre um valor fixo no .NET Native, que é diferente do valor configurável padrão no .NET para aplicativos Windows 8.x.

Algumas diferenças de comportamento adicionais são abordadas nas subseções a seguir.

Procuração

A HttpBaseProtocolFilter classe não suporta configurar ou substituir o proxy por solicitação. Isso significa que todas as solicitações no .NET Native usam o servidor proxy configurado pelo sistema ou nenhum servidor proxy, dependendo do valor da HttpClientHandler.UseProxy propriedade. No .NET para aplicativos do Windows 8.x, o servidor proxy é definido pela HttpClientHandler.Proxy propriedade. No .NET Native, definir o HttpClientHandler.Proxy para um valor diferente de null lança uma PlatformNotSupportedException exceção. A HttpClientHandler.SupportsProxy propriedade retorna false no .NET Native, enquanto retorna true no .NET Framework padrão para aplicativos do Windows 8.x.

Redirecionamento Automático

A HttpBaseProtocolFilter classe não permite que o número máximo de redirecionamentos automáticos seja configurado. O valor da HttpClientHandler.MaxAutomaticRedirections propriedade é 50 por padrão no .NET padrão para aplicativos do Windows 8.x e pode ser modificado. No .NET Native, o valor dessa propriedade é 10, e tentar modificá-la gera uma PlatformNotSupportedException exceção. A HttpClientHandler.SupportsRedirectConfiguration propriedade retorna false no .NET Native, enquanto retorna true no .NET para aplicativos do Windows 8.x.

Descompressão automática

O .NET para aplicativos do Windows 8.x permite que você defina a HttpClientHandler.AutomaticDecompression propriedade como Deflate, GZip, ambos Deflate e GZip, ou None. O .NET Native suporta apenas Deflate juntamente com GZip, ou None. Tentar definir a propriedade AutomaticDecompression como Deflate ou GZip sozinha silenciosamente a define para Deflate e GZip.

Biscoitos

O tratamento de cookies é realizado simultaneamente por HttpClient e pelo WinINet. Os cookies do CookieContainer são combinados com cookies no cache de cookies do WinINet. Remover um cookie de CookieContainer impede HttpClient de enviar o cookie, mas se o cookie já tiver sido visto pelo WinINet, e os cookies não tenham sido eliminados pelo utilizador, o WinINet irá enviá-lo. Não é possível remover programaticamente um cookie do WinINet usando o HttpClient, HttpClientHandler, ou CookieContainer API. Definir a HttpClientHandler.UseCookies propriedade para false faz com que apenas HttpClient pare de enviar cookies; o WinINet ainda pode incluir os seus cookies na solicitação.

Credenciais

No .NET para aplicativos do Windows 8.x, as HttpClientHandler.UseDefaultCredentials propriedades e HttpClientHandler.Credentials funcionam de forma independente. Além disso, a Credentials propriedade aceita qualquer objeto que implementa a ICredentials interface. No .NET Native, definir a UseDefaultCredentials propriedade para true faz com que a Credentials propriedade se torne null. Além disso, a Credentials propriedade pode ser definida apenas como null, DefaultCredentials, ou um objeto do tipo NetworkCredential. Atribuir qualquer outro objeto ICredentials, o mais popular dos quais é CredentialCache, à propriedade Credentials gera um PlatformNotSupportedException.

Outras funcionalidades não suportadas ou não configuráveis

No .NET Nativo:

Diferenças de interoperabilidade

APIs obsoletas

Várias APIs usadas com pouca frequência para interoperabilidade com código gerenciado foram preteridas. Quando utilizadas com o .NET Native, essas APIs podem resultar numa NotImplementedException exceção ou num PlatformNotSupportedException erro do compilador. No .NET para aplicativos do Windows 8.x, essas APIs são marcadas como obsoletas, embora chamá-las gere um aviso do compilador em vez de um erro do compilador.

As APIs preteridas para empacotamento de VARIANT incluem:

UnmanagedType.Struct é suportado, mas lança uma exceção em alguns cenários, como quando é usado com variantes IDispatch ou byref.

As APIs preteridas para suporte a IDispatch incluem:

As APIs obsoletas para eventos COM clássicos incluem:

As APIs depreciadas na interface System.Runtime.InteropServices.ICustomQueryInterface, que não são suportadas no .NET Native, incluem:

Outros recursos de interoperabilidade sem suporte incluem:

APIs de marshaling raramente usadas:

Invocação de plataforma e compatibilidade da interoperabilidade COM

A maioria dos cenários de invocação de plataforma e interoperabilidade COM ainda são suportados no .NET Native. Em particular, toda a interoperabilidade com as APIs do Windows Runtime (WinRT) e todo o marshalling necessário para o Windows Runtime é suportado. Isso inclui a mobilização de apoio para:

No entanto, o .NET Native não suporta o seguinte:

Não há suporte para o uso da reflexão para invocar um método de invocação de plataforma. Você pode contornar essa limitação encapsulando a chamada de método em outro método e usando reflexão para chamar o wrapper em vez disso.

Outras diferenças das APIs .NET para aplicativos do Windows 8.x

Esta seção lista as APIs restantes que não são suportadas no .NET Native. O maior conjunto de APIs sem suporte é o Windows Communication Foundation (WCF).

DataAnnotations (System.ComponentModel.DataAnnotations)

Os tipos nos System.ComponentModel.DataAnnotations namespaces e System.ComponentModel.DataAnnotations.Schema não são suportados no .NET Native. Estes incluem os seguintes tipos que estão presentes no .NET para aplicativos do Windows 8.x:

Visual Basic

Visual Basic não é suportado atualmente no .NET Native. Os seguintes tipos nos Microsoft.VisualBasic namespaces e Microsoft.VisualBasic.CompilerServices não estão disponíveis no .NET Native:

Contexto de Reflexão (namespace System.Reflection.Context)

A System.Reflection.Context.CustomReflectionContext classe não é suportada no .NET Native.

RTC (System.Net.Http.Rtc)

A System.Net.Http.RtcRequestFactory classe não é suportada no .NET Native.

Windows Communication Foundation (WCF) (System.ServiceModel.*)

Os tipos nos namespaces System.ServiceModel.* não são suportados no .NET Native. Estes incluem os seguintes tipos:

Diferenças nos serializadores

As seguintes diferenças dizem respeito à serialização e desserialização com as classes DataContractSerializer, DataContractJsonSerializer e XmlSerializer:

Diferenças do Visual Studio

Exceções e depuração

Quando você está executando aplicativos compilados usando o .NET Native no depurador, as exceções de primeira chance são habilitadas para os seguintes tipos de exceção:

Criando aplicativos

Use as ferramentas de compilação x86 que são usadas por padrão pelo Visual Studio. Não recomendamos o uso das ferramentas AMD64 MSBuild, que são encontradas em C:\Program Files (x86)\MSBuild\12.0\bin\amd64; Isso pode criar problemas de compilação.

Criadores de perfil

  • O Visual Studio CPU Profiler e o XAML Memory Profiler não exibem Just-My-Code corretamente.

  • O XAML Memory Profiler não exibe com precisão dados de heap gerenciados.

  • O CPU Profiler não identifica corretamente os módulos e exibe nomes de funções prefixados.

Projetos de Biblioteca de Testes de Unidade

A habilitação do .NET Native em uma biblioteca de teste de unidade para um projeto de aplicativo do Windows 8.x não é suportada e faz com que o projeto falhe na compilação.

Ver também