Gostaria de reunir o máximo de informações possível sobre a versão da API no .NET / CLR e, especificamente, sobre como as alterações na API quebram ou não os aplicativos clientes. Primeiro, vamos definir alguns termos:
Alteração na API - uma alteração na definição publicamente visível de um tipo, incluindo qualquer um de seus membros públicos. Isso inclui alterar o tipo e os nomes dos membros, alterar o tipo base de um tipo, adicionar / remover interfaces da lista de interfaces implementadas de um tipo, adicionar / remover membros (incluindo sobrecargas), alterar a visibilidade do membro, renomear métodos e renomear parâmetros, adicionar valores padrão para parâmetros de método, adicionando / removendo atributos em tipos e membros e adicionando / removendo parâmetros de tipo genérico em tipos e membros (perdi alguma coisa?). Isso não inclui nenhuma alteração nos corpos dos membros ou quaisquer alterações nos membros privados (ou seja, não levamos em consideração a Reflexão).
Quebra no nível binário - uma alteração na API que resulta em assemblies de cliente compilados em relação à versão mais antiga da API, potencialmente não carregando com a nova versão. Exemplo: alterando a assinatura do método, mesmo que permita ser chamado da mesma maneira que antes (ou seja: void para retornar sobrecargas nos valores padrão do tipo / parâmetro).
Quebra no nível da fonte - uma alteração na API que resulta no código existente gravado para compilar em uma versão mais antiga da API, potencialmente não compilando com a nova versão. Entretanto, os assemblies de cliente já compilados funcionam como antes. Exemplo: adicionando uma nova sobrecarga que pode resultar em ambiguidade nas chamadas de método que não eram ambíguas anteriormente.
Mudança de semântica silenciosa no nível de origem - uma mudança de API que resulta em código existente gravado para compilar em uma versão mais antiga da API muda silenciosamente sua semântica, por exemplo, chamando um método diferente. No entanto, o código deve continuar a compilar sem avisos / erros, e os assemblies compilados anteriormente devem funcionar como antes. Exemplo: implementando uma nova interface em uma classe existente que resulta em uma sobrecarga diferente sendo escolhida durante a resolução da sobrecarga.
O objetivo final é catalogar o máximo possível de alterações de API de semântica de interrupção e silêncio e descrever o efeito exato da quebra e quais idiomas são e não são afetados por ela. Para expandir o último: enquanto algumas mudanças afetam todos os idiomas universalmente (por exemplo, adicionar um novo membro a uma interface interrompe as implementações dessa interface em qualquer idioma), algumas requerem semânticas de idiomas muito específicas para entrar em jogo para obter uma pausa. Isso geralmente envolve sobrecarga de método e, em geral, qualquer coisa relacionada a conversões implícitas de tipo. Parece não haver nenhuma maneira de definir o "denominador menos comum" aqui, mesmo para idiomas compatíveis com CLS (ou seja, aqueles que estão em conformidade pelo menos com as regras do "consumidor CLS", conforme definido nas especificações da CLI) - embora eu ' Eu aprecio se alguém me corrigir como errado aqui - então isso terá que ir idioma por idioma. Os de maior interesse são naturalmente os que vêm com o .NET pronto para uso: C #, VB e F #; mas outros, como IronPython, IronRuby, Delphi Prism etc. também são relevantes. Quanto mais difícil for o caso, mais interessante será - coisas como remover membros são bastante evidentes, mas interações sutis entre, por exemplo, sobrecarga de método, parâmetros opcionais / padrão, inferência do tipo lambda e operadores de conversão podem ser muito surpreendentes às vezes.
Alguns exemplos para o kickstart:
Adicionando novas sobrecargas de método
Tipo: quebra no nível da fonte
Idiomas afetados: C #, VB, F #
API antes da alteração:
public class Foo
{
public void Bar(IEnumerable x);
}
API após alteração:
public class Foo
{
public void Bar(IEnumerable x);
public void Bar(ICloneable x);
}
Exemplo de código do cliente trabalhando antes da alteração e quebrado após ela:
new Foo().Bar(new int[0]);
Incluindo novas sobrecargas implícitas do operador de conversão
Tipo: quebra no nível da fonte.
Idiomas afetados: C #, VB
Idiomas não afetados: F #
API antes da alteração:
public class Foo
{
public static implicit operator int ();
}
API após alteração:
public class Foo
{
public static implicit operator int ();
public static implicit operator float ();
}
Exemplo de código do cliente trabalhando antes da alteração e quebrado após ela:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
Notas: F # não está quebrado, porque ele não tem qualquer apoio nível de linguagem para operadores sobrecarregados, nem explícitas nem implícitas - ambos tem que ser chamado diretamente como op_Explicit
e op_Implicit
métodos.
Adicionando novos métodos de instância
Tipo: a semântica silenciosa no nível da fonte é alterada.
Idiomas afetados: C #, VB
Idiomas não afetados: F #
API antes da alteração:
public class Foo
{
}
API após alteração:
public class Foo
{
public void Bar();
}
Código de cliente de amostra que sofre uma alteração semântica silenciosa:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
Notas: O F # não está quebrado, porque não possui suporte para o nível de idioma ExtensionMethodAttribute
e requer que os métodos de extensão CLS sejam chamados como métodos estáticos.