Considerações sobre versão e atualização para desenvolvedores de C#

A compatibilidade é um objetivo importante à medida que novos recursos são adicionados à linguagem C#. Em quase todos os casos, o código existente pode ser recompilado com uma nova versão do compilador sem qualquer problema. A equipe de tempo de execução do .NET também tem o objetivo de garantir a compatibilidade para bibliotecas atualizadas. Em quase todos os casos, quando seu aplicativo é iniciado a partir de um tempo de execução atualizado com bibliotecas atualizadas, o comportamento é exatamente o mesmo das versões anteriores.

A versão de idioma usada para compilar seu aplicativo normalmente corresponde ao moniker de estrutura de destino de tempo de execução (TFM) referenciado em seu projeto. Para obter mais informações sobre como alterar a versão de idioma padrão, consulte o artigo intitulado Configure your language version. Esse comportamento padrão garante compatibilidade máxima.

Quando as alterações de quebra são introduzidas, elas são classificadas como:

  • Alteração de quebra binária: uma alteração de quebra binária causa um comportamento diferente, incluindo possivelmente falhas, em seu aplicativo ou biblioteca quando iniciada usando um novo tempo de execução. Você deve recompilar seu aplicativo para incorporar essas alterações. O binário existente não funcionará corretamente.
  • Alteração de quebra de código-fonte: uma alteração de quebra de código-fonte altera o significado do seu código-fonte. Você precisa fazer edições no código-fonte antes de compilar seu aplicativo com a versão mais recente do idioma. Seu binário existente será executado corretamente com o host e o tempo de execução mais recentes. Observe que, para a sintaxe da linguagem, uma alteração de quebra de fonte também é uma alteração comportamental, conforme definido nas alterações de quebra de tempo de execução.

Quando uma alteração de quebra binária afeta seu aplicativo, você deve recompilá-lo, mas não precisa editar nenhum código-fonte. Quando uma alteração de quebra de origem afeta seu aplicativo, o binário existente ainda é executado corretamente em ambientes com o tempo de execução e bibliotecas atualizados. No entanto, você deve fazer alterações de origem para recompilar com a nova versão de idioma e tempo de execução. Se uma alteração for quebra de código-fonte e quebra binária, você deverá recompilar seu aplicativo com a versão mais recente e fazer atualizações de origem.

Devido ao objetivo de evitar alterações significativas pela equipe de linguagem C# e pela equipe de tempo de execução, atualizar seu aplicativo geralmente é uma questão de atualizar o TFM e reconstruir o aplicativo. No entanto, para bibliotecas distribuídas publicamente, você deve avaliar cuidadosamente sua política para TFMs e versões de idiomas suportados. Você pode estar criando uma nova biblioteca com recursos encontrados na versão mais recente e precisa garantir que os aplicativos criados usando versões anteriores do compilador possam usá-la. Ou você pode estar atualizando uma biblioteca existente e muitos de seus usuários podem não ter versões atualizadas ainda.

Introdução de alterações significativas nas suas bibliotecas

Ao adotar novos recursos de linguagem na API pública da sua biblioteca, você deve avaliar se a adoção do recurso introduz uma alteração binária ou de quebra de fonte para os usuários da sua biblioteca. Quaisquer alterações à sua implementação interna que não apareçam nas public interfaces ou protected são compatíveis.

Nota

Se você usar o System.Runtime.CompilerServices.InternalsVisibleToAttribute para habilitar tipos para ver membros internos, os membros internos podem introduzir alterações de quebra.

Uma alteração de quebra binária requer que os usuários recompilem seu código para usar a nova versão. Por exemplo, considere este método público:

public double CalculateSquare(double value) => value * value;

Se você adicionar o in modificador ao método, essa é uma alteração de quebra binária:

public double CalculateSquare(in double value) => value * value;

Os usuários devem recompilar qualquer aplicativo que usa o CalculateSquare método para que a nova biblioteca funcione corretamente.

Uma alteração de quebra de fonte exige que os usuários alterem o código antes de recompilar. Por exemplo, considere este tipo:

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName) => (FirstName, LastName) = (firstName, lastName);

    // other details omitted
}

Em uma versão mais recente, você gostaria de aproveitar os membros sintetizados gerados para record tipos. Faça a seguinte alteração:

public record class Person(string FirstName, string LastName);

A alteração anterior requer alterações para qualquer tipo derivado de Person. Todas essas declarações devem adicionar o record modificador às suas declarações.

Impacto das alterações significativas

Quando você adiciona uma alteração de quebra binária à sua biblioteca, você força todos os projetos que usam sua biblioteca para recompilar. No entanto, nenhum código-fonte nesses projetos precisa ser alterado. Como resultado, o impacto da mudança de rutura é razoavelmente pequeno para cada projeto.

Quando você faz uma alteração de quebra de código-fonte em sua biblioteca, você exige que todos os projetos façam alterações de origem para usar sua nova biblioteca. Se a alteração necessária exigir novos recursos de idioma, você forçará esses projetos a atualizar para a mesma versão de idioma e TFM que você está usando agora. Você exigiu mais trabalho para seus usuários e, possivelmente, os forçou a atualizar também.

O impacto de qualquer alteração significativa que você faça depende do número de projetos que dependem da sua biblioteca. Se sua biblioteca for usada internamente por alguns aplicativos, você poderá reagir a quaisquer alterações significativas em todos os projetos afetados. No entanto, se a sua biblioteca for transferida publicamente, deve avaliar o impacto potencial e considerar alternativas:

  • Você pode adicionar novas APIs paralelas às APIs existentes.
  • Você pode considerar compilações paralelas para TFMs diferentes.
  • Você pode considerar multi-targeting.