Compartilhar 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 executada para aplicativos do Windows 8.x (também chamados anteriormente de aplicativos da Microsoft Store) pelo compilador JIT (just-in-time) ou pelo Gerador de Imagem Nativa (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 nos aplicativos .NET para Windows 8.x também funcionam com o .NET Native. No entanto, em alguns casos, você pode encontrar alterações de comportamento. Este documento discute essas diferenças entre os aplicativos padrão do .NET para Windows 8.x e o .NET Native nas seguintes áreas:

Diferenças de runtime geral

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

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

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

  • Loops infinitos sem fazer uma chamada (por exemplo, while(true);) em qualquer thread interromper o aplicativo. Da mesma forma, esperas grandes ou infinitas podem interromper o aplicativo.

  • Determinados ciclos de inicialização genéricos não geram exceções no .NET Native. Por exemplo, o código a seguir gera uma exceção TypeLoadException 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, desde que sua implementação de backup seja diferente, você não poderá convertê-lo para o mesmo conjunto de tipos como realizado em outras plataformas .NET Framework. Por exemplo, em alguns casos, você não poderá converter o objeto de interface IEnumerable<T> 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 de 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 tornar o código local do aplicativo para obter o máximo desempenho. No entanto, os tamanhos binários precisam permanecer pequenos para que todo o .NET Framework não seja 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 reflexão e programação dinâmica. No entanto, nem todos os tipos podem ser marcados para reflexão, pois isso tornaria o tamanho do código gerado muito grande (especialmente porque há suporte para a reflexão sobre APIs públicas no .NET Framework). O compilador .NET Native faz escolhas inteligentes sobre quais tipos devem dar suporte à reflexão e mantém os metadados e gera código apenas para esses tipos.

Por exemplo, a associação de dados necessita de um aplicativo para conseguir mapear nomes de propriedade às funções. Em aplicativos .NET para Windows 8.x, o Common Language Runtime usa automaticamente a reflexão para fornecer essa funcionalidade para tipos gerenciados e tipos nativos disponíveis publicamente. No .NET Native, o compilador inclui automaticamente metadados para tipos aos quais você associa 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 tem suporte 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 do .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, especialmente nos seguintes casos:

  • Algumas construções 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 deverá ser especificado por diretivas de runtime. Isso não é apenas porque todo o código deve ser incluído, mas sim porque a reflexão em tipos genéricos pode formar um ciclo infinito (por exemplo, quando um método genérico é invocado em um tipo genérico).

Observação

Diretivas de runtime são definidas em um arquivo de diretivas de runtime (.rd.xml). Para obter mais informações sobre como usar essa arquivo, consulte Introdução. Para obter informações sobre as diretivas de runtime, consulte Referência do arquivo de configuração das diretivas de runtime (rd.xml).

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 dar suporte à reflexão.

Há várias outras diferenças individuais relacionadas à reflexão no comportamento entre o .NET para aplicativos do Windows 8.x e o .NET Native.

No .NET Native:

  • Não há suporte para reflexão privada sobre tipos e membros da biblioteca de classes do .NET Framework. No entanto, é possível refletir sobre seus próprios tipos e membros privados, bem como tipos e membros em bibliotecas de terceiros.

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

  • Membros públicos nas estruturas RuntimeFieldHandle e RuntimeMethodHandle não são suportadas. 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 membros ocultos em classes base e, portanto, podem ser substituídos sem substituições explícitas. Isso também é verdadeiro para outros métodos RuntimeReflectionExtensions.GetRuntime*.

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

  • Você não pode usar reflexão para invocar os membros que têm parâmetros de ponteiro.

  • Você não pode usar reflexão 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 gera um ArgumentException .TargetParameterCountException

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

Cenários e APIs sem suporte

As seções a seguir listam cenários não suportados e APIs de desenvolvimento geral, interoperabilidade e tecnologias como HTTPClient e Windows Communication Foundation (WCF):

Diferenças de desenvolvimento geral

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. Em aplicativos .NET para 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, pois o .NET Native gera a implementação em tempo de compilação. No entanto, substituir esses métodos, mas chamar a implementação de classe base, resulta em 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, eles podem ser criados em IL.)

matrizes

  • Não há suporte para matrizes com um limite inferior diferente de zero. Normalmente, esses conjuntos são criados ao chamar a sobrecarga Array.CreateInstance(Type, Int32[], Int32[]).

  • A criação dinâmica de matrizes multidimensionais não tem suporte. Essas matrizes geralmente são criados 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, com valor da propriedade Array.Rank quatro ou superior. Use matrizes denteadas (uma matriz de matrizes) em vez disso. Por exemplo, array[x,y,z] é inválido, mas array[x][y][z] não é.

  • Variação de matrizes multidimensionais não é suportada e causa uma exceção InvalidCastException no tempo de execução.

Genéricos

  • A expansão do tipo genérico infinito resulta em um erro do compilador. Por exemplo, esse código falha ao compilar:

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

Ponteiros

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

  • Você não pode usar reflexão para obter ou definir um campo de ponteiro.

Serialização

O atributo KnownTypeAttribute(String) não é suportado. Use o atributo KnownTypeAttribute(Type) em vez disso.

Recursos

O uso de recursos localizados com a classe EventSource não é suportado. A propriedade EventSourceAttribute.LocalizationResources não define os recursos localizados.

Representantes

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

APIs diversas

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

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

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

Diferenças do HttpClient

No .NET Native, a HttpClientHandler classe usa internamente o WinINet (por meio da HttpBaseProtocolFilter classe) em vez das WebRequest classes e WebResponse usadas nos aplicativos .NET para Windows 8.x padrão. O WinINet não oferece suporte a todas as opções de configuração que a classe HttpClientHandler suporta. Como resultado:

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

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

Algumas diferenças comportamentais adicionais são abrangidas nas subseções a seguir.

Proxy

A HttpBaseProtocolFilter classe não dá suporte à configuração ou substituição do 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. Em aplicativos .NET para Windows 8.x, o servidor proxy é definido pela HttpClientHandler.Proxy propriedade. No .NET Native, definir o HttpClientHandler.Proxy como um valor diferente de null gera 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 nos aplicativos .NET para Windows 8.x padrão 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.

Descompactação automática

Os aplicativos do .NET para Windows 8.x permitem que você defina a HttpClientHandler.AutomaticDecompression propriedade como Deflate, GZip, e Deflate GZip, ou None. O .NET Native só dá suporte Deflate a GZip, ou None. Tentar definir a propriedade AutomaticDecompression para Deflate ou GZip sozinho silenciosamente define-a para Deflate e GZip.

Cookies

A manipulação de cookies é executada simultaneamente por HttpClient e WinINet. Os cookies do CookieContainer são combinados com cookies no cache de cookies do WinINet. Remover um cookie do CookieContainer impede que HttpClient envie o cookie, mas se o cookie já foi visto pelo WinINet e os cookies não foram excluídos pelo usuário, o WinINet o envia. Não é possível programar a remoção automática de um cookie do WinINet usando a API HttpClient, HttpClientHandler ou CookieContainer. Definir a propriedade HttpClientHandler.UseCookies para false faz com que apenas HttpClient pare de enviar cookies; o WinINet ainda pode incluir os cookies na solicitação.

Credenciais

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

Outros recursos sem suporte ou não configuráveis

No .NET Native:

Diferenças de interoperabilidade

APIs preteridas

Várias APIs pouco usadas para a interoperabilidade com código gerenciado foram preteridas. Quando usadas com o .NET Native, essas APIs podem gerar uma NotImplementedException exceção ou PlatformNotSupportedException resultar em um erro do compilador. Em aplicativos .NET para 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 obsoletas para VARIANT marshaling incluem:

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

APIs preteridas para suporte a IDispatch incluem:

As APIs preteridas para eventos COM clássicos incluem:

APIs preteridas System.Runtime.InteropServices.ICustomQueryInterface na interface, que não têm suporte no .NET Native, incluem:

Outros recursos de interoperabilidade sem suporte incluem:

APIs de marshaling raramente usadas:

Invocação de plataforma e compatibilidade de interoperabilidade COM

A maioria dos cenários de invocação de plataforma e interoperabilidade COM ainda tem suporte no .NET Native. Em especial, toda a interoperabilidade com APIs do Windows Runtime (WinRT) APIs e todo o marshaling necessário para o Windows Runtime são suportados. Isso inclui suporte a marshaling para:

No entanto, o .NET Native não dá suporte ao seguinte:

Usar reflexão para invocar um método de invocação de plataforma não é suportado. Você pode contornar essa limitação envolvendo 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 do .NET para aplicativos do Windows 8.x

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

DataAnnotations (System.ComponentModel.DataAnnotations)

Não há suporte para System.ComponentModel.DataAnnotations os tipos nos namespaces e System.ComponentModel.DataAnnotations.Schema no .NET Native. Isso inclui os seguintes tipos que estão presentes no .NET para aplicativos do Windows 8.x:

Visual Basic

No momento, não há suporte para o Visual Basic 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)

Não há suporte para a System.Reflection.Context.CustomReflectionContext classe no .NET Native.

RTC (System.Net.Http.Rtc)

Não há suporte para a System.Net.Http.RtcRequestFactory classe no .NET Native.

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

Não há suporte para os tipos nos namespaces System.ServiceModel.* no .NET Native. Isso inclui os seguintes tipos:

Diferenças nos serializadores

As seguintes diferenças envolvem serialização e desserialização com e 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 criação x86 usadas por padrão pelo Visual Studio. Não recomendamos usar as ferramentas AMD64 MSBuild, que se encontram em C:\Program Files (x86)\MSBuild\12.0\bin\amd64, pois podem criar problemas de compilação.

Criadores de perfil

  • O criador de perfil de CPU do Visual Studio e o criador de perfil de memória XAML não exibem o Just-My-Code corretamente.

  • O gerador de perfil de memória XAML não exibe corretamente os dados da pilha gerenciada.

  • O gerador de perfil de CPU não identifica corretamente módulos e exibe nomes de função de prefixo.

Projetos da biblioteca de teste de unidade

Não há suporte para habilitar o .NET Native em uma biblioteca de teste de unidade para um projeto de aplicativo do Windows 8.x e faz com que o projeto falhe ao ser compilado.

Confira também