Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Ao longo de seu histórico, o .NET tentou manter um alto nível de compatibilidade de versão para versão e entre implementações do .NET. Embora o .NET 5 (e o .NET Core) e versões posteriores possam ser considerados como uma nova tecnologia em comparação com o .NET Framework, dois fatores principais limitam a capacidade dessa implementação do .NET de divergir do .NET Framework:
- Muitos desenvolvedores originalmente desenvolveram ou continuam a desenvolver aplicativos para o .NET Framework. Eles esperam um comportamento consistente entre implementações do .NET.
- Os projetos de biblioteca do .NET Standard permitem que os desenvolvedores criem bibliotecas direcionadas a APIs comuns compartilhadas pelo .NET Framework e .NET 5 (e .NET Core) e versões posteriores. Os desenvolvedores esperam que uma biblioteca usada em um aplicativo .NET se comporte de forma idêntica à mesma biblioteca usada em um aplicativo .NET Framework.
Juntamente com a compatibilidade entre implementações do .NET, os desenvolvedores esperam um alto nível de compatibilidade entre versões de uma determinada implementação do .NET. Em particular, o código escrito para uma versão anterior do .NET Core deve ser executado perfeitamente no .NET 5 ou em uma versão posterior. Na verdade, muitos desenvolvedores esperam que as novas APIs encontradas em versões recém-lançadas do .NET também sejam compatíveis com as versões de pré-lançamento nas quais essas APIs foram introduzidas.
Este artigo descreve as alterações que afetam a compatibilidade e a maneira como a equipe do .NET avalia cada tipo de alteração. Compreender como a equipe do .NET aborda as possíveis alterações de falha é particularmente útil para os desenvolvedores que abrem solicitações de pull que modificam o comportamento das APIs .NET existentes.
As seções a seguir descrevem as categorias de alterações feitas nas APIs do .NET e seu impacto na compatibilidade do aplicativo. As alterações são permitidas (✔️), não permitidas (❌) ou exigem julgamento e uma avaliação de quão previsível, óbvio e consistente era o comportamento anterior (❓).
Observação
- Além de servir como um guia de como as alterações nas bibliotecas do .NET são avaliadas, os desenvolvedores de biblioteca também podem usar esses critérios para avaliar as alterações em suas bibliotecas direcionadas a várias implementações e versões do .NET.
- Para obter informações sobre as categorias de compatibilidade, por exemplo, compatibilidade com versões anteriores e futuras, consulte Como as alterações de código podem afetar a compatibilidade.
Modificações no contrato público
As alterações nessa categoria modificam a área de superfície pública de um tipo. A maioria das alterações nessa categoria não são permitidas, pois violam a compatibilidade com versões anteriores (a capacidade de um aplicativo que foi desenvolvido com uma versão anterior de uma API a ser executada sem recompilação em uma versão posterior).
Tipos
✔️ PERMITIDO: Remover uma implementação de interface de um tipo quando a interface já estiver implementada por um tipo base
❓ REQUER JULGAMENTO: Adicionando uma nova implementação de interface a um tipo
Essa é uma alteração aceitável porque não afeta negativamente os clientes existentes. Todas as alterações no tipo devem funcionar dentro dos limites de alterações aceitáveis definidas aqui para que a nova implementação permaneça aceitável. É necessário extrema cautela ao adicionar interfaces que afetam diretamente a capacidade de um designer ou serializador gerar código ou dados que não podem ser consumidos no nível inferior. Um exemplo é a ISerializable interface.
❓ REQUER JULGAMENTO: Introdução a uma nova classe base
Um tipo pode ser introduzido em uma hierarquia entre dois tipos existentes se não introduzir novos membros abstratos ou alterar a semântica ou o comportamento dos tipos existentes. Por exemplo, no .NET Framework 2.0, a DbConnection classe tornou-se uma nova classe base para SqlConnection, que anteriormente derivava diretamente de Component.
✔️ PERMITIDO: mover um tipo de um assembly para outro
O assembly antigo precisa ser marcado com o TypeForwardedToAttribute que aponta para o novo assembly.
✔️ PERMITIDO: alterar um tipo de struct para um tipo
readonly structNão é permitido alterar um
readonly structtipo para umstructtipo.✔️ PERMITIDO: Adicionar a palavra-chave sealed ou abstrata a um tipo quando não houver construtores acessíveis (públicos ou protegidos)
✔️ PERMITIDO: Expansão da visibilidade de um tipo
❌ NÃO PERMITIDO: alterar o namespace ou nome de um tipo
❌ NÃO PERMITIDO: renomear ou remover um tipo público
Isso interrompe todo o código que usa o tipo renomeado ou removido.
Observação
Em casos raros, o .NET pode remover uma API pública. Para obter mais informações, consulte a remoção de API no .NET. Para obter informações sobre a política de suporte do .NET, consulte a Política de Suporte do .NET.
❌ NÃO PERMITIDO: Alterar o tipo subjacente de uma enumeração
Esta é uma alteração significativa de tempo de compilação e comportamental, bem como uma alteração significativa binária que pode tornar os argumentos de atributo inseparáveis.
❌ NÃO PERMITIDO: selar um tipo que estava sem selo
❌ DISALLOWED: Adicionar uma interface ao conjunto de tipos base de uma interface
Se uma interface passar a implementar uma interface que anteriormente não implementava, todos os tipos que implementaram a versão original da interface ficarão incompatíveis.
❓ REQUER JULGAMENTO: removendo uma classe do conjunto de classes base ou uma interface do conjunto de interfaces implementadas
Há uma exceção à regra para remoção de interface: você pode adicionar a implementação de uma interface que deriva da interface removida. Por exemplo, você pode remover IDisposable se o tipo ou interface agora implementa IComponent, que implementa IDisposable.
❌ NÃO PERMITIDO: alterar um tipo
readonly structpara um tipo structNo entanto, a alteração de um
structtipo para umreadonly structtipo é permitida.❌ NÃO PERMITIDO: alterar um tipo struct para um
ref structe vice-versa❌ NÃO PERMITIDO: reduzir a visibilidade de um tipo
No entanto, aumentar a visibilidade de um tipo é permitido.
Membros
✔️ PERMITIDO: expandir a visibilidade de um membro que não é virtual
✔️ PERMITIDO: Adicionar um membro abstrato a um tipo público que não tenha construtores acessíveis (públicos ou protegidos) ou que o tipo seja lacrado
No entanto, não é permitido adicionar um membro abstrato a um tipo que tenha construtores acessíveis (públicos ou protegidos) e não seja
sealed.✔️ PERMITIDO: Restringir a visibilidade de um membro protegido quando o tipo não tiver construtores acessíveis (públicos ou protegidos) ou se o tipo estiver lacrado
✔️ PERMITIDO: Mover um membro para uma classe mais alta na hierarquia do que o tipo do qual ele foi removido
✔️ PERMITIDO: adicionar ou remover uma substituição
A introdução de uma substituição pode fazer com que os consumidores anteriores pulem a substituição ao chamar a base.
✔️ PERMITIDO: Adicionar um construtor a uma classe, juntamente com um construtor sem parâmetros se a classe anteriormente não tivesse construtores
No entanto, não é permitido adicionar um construtor a uma classe que anteriormente não tinha construtores sem adicionar o construtor sem parâmetros.
✔️ PERMITIDO: Mudança de um valor de retorno
ref readonlypararef(exceto para métodos ou interfaces virtuais)✔️ PERMITIDO: Remover readonly de um campo, a menos que o tipo estático do campo seja um tipo de valor mutável
✔️ PERMITIDO: Chamar um novo evento que não foi definido anteriormente
❓ REQUER JULGAMENTO: Adicionar um novo campo de instância a um tipo
Essa alteração afeta a serialização.
❌ NÃO PERMITIDO: renomear ou remover um membro ou parâmetro público
Isso interrompe todo o código que usa o membro ou parâmetro renomeado ou removido.
Isso inclui remover ou renomear um getter ou setter de uma propriedade, bem como renomear ou remover membros de enumeração.
❓ REQUER JULGAMENTO: adicionar um membro a uma interface
Embora seja uma alteração significativa no sentido de que ele eleva sua versão mínima do .NET para o .NET Core 3.0 (C# 8.0), que é quando os DIMs ( membros de interface) padrão foram introduzidos, a adição de um membro estático, não abstrato e não virtual a uma interface é permitido.
Se você fornecer uma implementação, adicionar um novo membro a uma interface existente não necessariamente resultará em falhas de compilação em assemblies downstream. No entanto, nem todos os idiomas dão suporte a DIMs. Além disso, em alguns cenários, o runtime não pode decidir qual membro de interface padrão invocar. Em alguns cenários, as interfaces são implementadas por
ref structtipos. Comoref structos tipos não podem ser encaixoados, eles não podem ser convertidos em tipos de interface. Portanto,ref structos tipos devem fornecer uma implementação implícita para cada membro da interface. Eles não podem usar a implementação padrão fornecida pela interface. Por esses motivos, use o julgamento ao adicionar um membro a uma interface existente.❌ NÃO PERMITIDO: Alteração do valor de uma constante pública ou membro de enumeração
❌ NÃO PERMITIDO: Mudar o tipo de uma propriedade, campo, parâmetro ou valor de retorno
❌ NÃO PERMITIDO: adicionar, remover ou alterar a ordem dos parâmetros
❌ NÃO PERMITIDO: adicionar ou remover a palavra-chave in, out ou ref de um parâmetro
❌ NÃO PERMITIDO: renomear um parâmetro (incluindo alterar a capitalização)
Isso é considerado interrupção por dois motivos:
Interrompe cenários de associação tardia, como o recurso de associação tardia no Visual Basic e dinâmico no C#.
Ele interrompe a compatibilidade de origem quando os desenvolvedores usam argumentos nomeados.
❌ NÃO PERMITIDO: alterar de um valor de retorno
refpara um valor de retornoref readonly❌️ NÃO PERMITIDO: alterar de um
ref readonlypara um valor de retornorefem um método ou interface virtual❌ NÃO PERMITIDO: adicionar ou remover abstract de um membro
❌ NÃO PERMITIDO: remover a palavra-chave virtual de um membro
❌ PROIBIDO: Adicionar a palavra-chave virtual a um membro
Embora isso geralmente não seja uma alteração significativa porque o compilador C# tende a emitir instruções de Linguagem Intermediária (IL) de callvirt para chamar métodos não virtuais (
callvirtexecuta uma verificação nula, enquanto uma chamada normal não), esse comportamento não é invariável por várias razões:- C# não é o único idioma direcionado ao .NET.
- O compilador C# tenta cada vez mais otimizar
callvirtpara uma chamada normal sempre que o método de destino não é virtual e provavelmente não é nulo (como um método acessado por meio do operador de propagação ?. null).
Tornar um método virtual significa que o código do consumidor em geral acabaria chamando-o não virtualmente.
❌ NÃO PERMITIDO: transformar um membro virtual em abstrato
Um membro virtual fornece uma implementação de método que pode ser substituída por uma classe derivada. Um membro abstrato não fornece nenhuma implementação e deve ser substituído.
❌ NÃO PERMITIDO: adicionando a palavra-chave sealed a um membro da interface
Adicionar
sealeda um membro de interface padrão o tornará não virtual, impedindo que a implementação de um tipo derivado desse membro seja chamada.❌ NÃO PERMITIDO: adicionar um membro abstrato a um tipo público que tenha construtores acessíveis (públicos ou protegidos) e não seja sealed
❌ NÃO PERMITIDO: Adicionar ou remover a palavra-chave static de um membro
❌ NÃO PERMITIDO: adicionar uma sobrecarga que impede uma sobrecarga existente e define um comportamento diferente
Isso interrompe os clientes existentes que estavam vinculados à sobrecarga anterior. Por exemplo, se uma classe tiver uma única versão de um método que aceite um UInt32, um consumidor existente será vinculado a essa sobrecarga ao passar um valor Int32. No entanto, se você adicionar uma sobrecarga que aceita um Int32, ao recompilar ou usar associação tardia, o compilador agora se associa à nova sobrecarga. Se resultar em um comportamento diferente, significa que essa é uma alteração significativa.
❌ NÃO PERMITIDO: adicionar um construtor a uma classe que anteriormente não tinha construtor sem adicionar o construtor sem parâmetros
❌️ NÃO PERMITIDO: adicionar readonly a um campo
❌ NÃO PERMITIDO: Reduzir a visibilidade de um membro
Isso inclui restringir a visibilidade de um membro protegido quando o tipo não tem construtores acessíveis (
publicouprotected) e o tipo não é sealed. Se esse não for o caso, a redução da visibilidade de um membro protegido será permitida.É permitido aumentar a visibilidade de um membro.
❌ NÃO PERMITIDO: alterar o tipo de um membro
O valor retornado de um método ou o tipo de uma propriedade ou campo não pode ser modificado. Por exemplo, a assinatura de um método que retorna um Object não pode ser alterada para retornar um Stringou vice-versa.
❌ PROIBIDO: adicionar um campo de instância a um struct que não contém campos não públicos
Se um struct tiver somente campos públicos ou não tiver nenhum campo, os chamadores poderão declarar locais desse tipo de struct sem chamar o construtor dele ou inicializar primeiro o local como
default(T), desde que todos os campos públicos sejam definidos no struct antes do primeiro uso. Adicionar campos públicos ou não públicos ao struct é uma alteração interruptiva de fonte para esses chamadores, pois o compilador agora exigirá que os campos adicionais sejam inicializados.Além disso, adicionar novos campos - sejam eles públicos ou privados - a uma estrutura sem campos ou com apenas campos públicos é uma alteração que quebra a compatibilidade binária para os usuários que aplicaram
[SkipLocalsInit]ao seu código. Como o compilador não estava ciente desses campos no tempo de compilação, ele poderia emitir IL, que não inicializa totalmente o struct e faz com que ele seja criado com base em dados de pilha não inicializados.Se um struct tiver campos não públicos, o compilador já imporá a inicialização por meio do construtor ou
default(T), e adicionar novos campos de instância não será uma alteração significativa.❌ NÃO PERMITIDO: disparar um evento existente quando ele nunca foi disparado antes
Alterações comportamentais
Assembléias
✔️ PERMITIDO: tornar um assembly portátil quando ainda há suporte para as mesmas plataformas
❌ PROIBIDO: Alterar o nome de um componente
❌ NÃO PERMITIDO: alterar a chave pública de um assembly
Propriedades, campos, parâmetros e valores retornados
✔️ PERMITIDO: alterando o valor de uma propriedade, campo, valor retornado ou parâmetro out para um tipo mais derivado
Por exemplo, um método que retorna um tipo de Object pode retornar uma String instância. (No entanto, a assinatura do método não pode ser alterada.)
✔️ PERMITIDO: Aumentando o intervalo de valores aceitos para uma propriedade ou parâmetro se o membro não for virtual
Embora o intervalo de valores que podem ser passados para o método ou retornados pelo membro possa ser expandido, o parâmetro ou tipo de membro não pode. Por exemplo, embora os valores passados para um método possam ser expandidos de 0 a 124 para 0 a 255, o tipo de parâmetro não pode ser alterado de Byte para Int32.
❌ NÃO PERMITIDO: aumentar o intervalo de valores aceitos para uma propriedade ou parâmetro se o membro for virtual
Essa alteração interrompe os membros substituídos existentes, que não funcionarão corretamente para o intervalo estendido de valores.
❌ NÃO PERMITIDO: reduzir o intervalo de valores aceitos de uma propriedade ou parâmetro
❌ NÃO PERMITIDO: aumentar o intervalo de valores retornados de uma propriedade, campo, valor de retorno ou parâmetro out
❌ NÃO PERMITIDO: alterar os valores retornados de uma propriedade, campo, valor de retorno ou parâmetro out
❌ PROIBIDO: Alterar o valor padrão de uma propriedade, campo ou parâmetro
Alterar ou remover um valor padrão de parâmetro não é uma quebra binária. Remover um valor padrão de parâmetro é uma quebra de origem e alterar um valor padrão de parâmetro pode resultar em uma quebra comportamental após a recompilação.
Por esse motivo, a remoção de valores padrão de parâmetro é aceitável no caso específico de "mover" esses valores padrão para uma nova sobrecarga de método para eliminar a ambiguidade. Por exemplo, considere um método
MyMethod(int a = 1)existente. Se você introduzir uma sobrecarga deMyMethodcom dois parâmetros opcionaisaeb, é possível preservar a compatibilidade movendo o valor padrão deapara a nova sobrecarga. Agora as duas sobrecargas sãoMyMethod(int a)eMyMethod(int a = 1, int b = 2). Esse padrão permite queMyMethod()seja compilado.❌ NÃO PERMITIDO: alterar a precisão de um valor retornado numérico
❓ REQUER JULGAMENTO: uma alteração na análise de entrada e geração de novas exceções (mesmo que o comportamento de análise não seja especificado na documentação
Exceções
✔️ PERMITIDO: lançar uma exceção mais derivada do que uma exceção existente
Como a nova exceção é uma subclasse de uma exceção existente, o código de tratamento de exceção anterior continua a lidar com a exceção. Por exemplo, no .NET Framework 4, os métodos de criação e recuperação de cultura começariam a lançar um CultureNotFoundException em vez de um ArgumentException se a cultura não pudesse ser encontrada. Como CultureNotFoundException deriva de ArgumentException, esta é uma mudança aceitável.
✔️ PERMITIDO: Lançar uma exceção mais específica do que NotSupportedException, NotImplementedException, NullReferenceException
✔️ PERMITIDO: lançar uma exceção que é considerada irrecuperável
Exceções irrecuperáveis não devem ser capturadas, mas precisam ser manipuladas por um manipulador de nível alto. Portanto, não se espera que os usuários tenham código que capture essas exceções explícitas. As exceções irrecuperáveis são:
✔️ PERMITIDO: Lançar uma nova exceção em uma nova trajetória de código
A exceção deve ser aplicada somente a um novo caminho de código executado com novos valores de parâmetro ou estado e que não pode ser executado pelo código existente direcionado à versão anterior.
✔️ PERMITIDO: Remover uma exceção para habilitar um comportamento mais robusto ou novos cenários
Por exemplo, um
Dividemétodo que anteriormente manipulava apenas valores positivos e lançava um ArgumentOutOfRangeException caso contrário pode ser alterado para dar suporte a valores negativos e positivos sem gerar uma exceção.✔️ PERMITIDO: Alterando o texto de uma mensagem de erro
Os desenvolvedores não devem confiar no texto das mensagens de erro, que também são alteradas com base na cultura do usuário.
❌ NÃO PERMITIDO: lançar uma exceção em qualquer outro caso não listado acima
❌ NÃO PERMITIDO: remover uma exceção em qualquer outro caso não listado acima
Atributos
✔️ PERMITIDO: Alterando o valor de um atributo que não é observável
❌ Não permitido: Alterar o valor de um atributo que é observável
❓ REQUER JULGAMENTO: Removendo um atributo
Na maioria dos casos, remover um atributo (como NonSerializedAttribute) é uma alteração significativa.
Suporte da plataforma
✔️ PERMITIDO: Suporte a uma operação em uma plataforma que anteriormente não tinha suporte
❌ NÃO PERMITIDO: não oferecer suporte ou agora exigir um pacote de serviço específico para uma operação que anteriormente tinha suporte em uma plataforma
Alterações de implementação internas
❓ REQUER ANÁLISE: alterar a área de superfície de um tipo interno
Essas alterações geralmente são permitidas, embora interrompa a reflexão privada. Em alguns casos, em que bibliotecas populares de terceiros ou um grande número de desenvolvedores dependem das APIs internas, essas alterações podem não ser permitidas.
❓ REQUER ANÁLISE: alterar a implementação interna de um membro
Essas alterações geralmente são permitidas, embora interrompa a reflexão privada. Em alguns casos, em que o código do cliente frequentemente depende de reflexão privada ou em que a alteração introduz efeitos colaterais não intencionais, essas alterações podem não ser permitidas.
✔️ PERMITIDO: Melhorando o desempenho de uma operação
A capacidade de modificar o desempenho de uma operação é essencial, mas essas alterações podem interromper o código que depende da velocidade atual de uma operação. Isso é particularmente verdadeiro para o código que depende do tempo das operações assíncronas. A alteração de desempenho não deve ter nenhum efeito sobre outro comportamento da API em questão; caso contrário, a alteração será interruptiva.
✔️ PERMITIDO: Indiretamente (e muitas vezes negativamente) alterando o desempenho de uma operação
Se a alteração em questão não for categorizada como falha por algum outro motivo, isso será aceitável. Muitas vezes, ações precisam ser executadas que possam incluir operações extras ou que adicionem novas funcionalidades. Isso quase sempre afetará o desempenho, mas pode ser essencial para que a API em questão funcione conforme o esperado.
❌ DISALLOWED: Alterando uma API síncrona para assíncrona (e vice-versa)
Alterações de código
✔️ PERMITIDO: Adicionar params a um parâmetro
❌ NÃO PERMITIDO: Alterando um struct para uma classe e vice-versa
❌ NÃO PERMITIDO: adicionar a instrução checked a um bloco de código
Essa alteração pode fazer com que o código executado anteriormente gere um OverflowException e seja inaceitável.
❌ NÃO PERMITIDO: remover params de um parâmetro
❌ NÃO PERMITIDO: alterar a ordem na qual os eventos são disparados
Os desenvolvedores podem esperar razoavelmente que os eventos sejam acionados na mesma ordem, e o código do desenvolvedor frequentemente depende da ordem na qual os eventos são disparados.
❌ NÃO PERMITIDO: remover o aumento de um evento em uma determinada ação
❌ NÃO PERMITIDO: Alteração do número de vezes que certos eventos são chamados
❌ NÃO PERMITIDO: adicionar o FlagsAttribute a um tipo de enumeração