Ler em inglês

Partilhar via


Controle de versão em C#

Neste tutorial, você aprenderá o que significa versionamento no .NET. Você também aprenderá os fatores a serem considerados ao controlar o controle de versão de sua biblioteca, bem como atualizar para uma nova versão de uma biblioteca.

Versão linguística

O compilador C# faz parte do SDK do .NET. Por padrão, o compilador escolhe a versão da linguagem C# que corresponde ao TFM escolhido para seu projeto. Se a versão do SDK for maior do que a estrutura escolhida, o compilador poderá usar uma versão de linguagem maior. Você pode alterar o padrão definindo o LangVersion elemento em seu projeto. Você pode aprender como em nosso artigo sobre opções do compilador.

Aviso

Definir o LangVersion elemento como latest é desencorajado. A latest configuração significa que o compilador instalado usa sua versão mais recente. Isso pode mudar de máquina para máquina, tornando as construções não confiáveis. Além disso, ele habilita recursos de linguagem que podem exigir recursos de tempo de execução ou biblioteca não incluídos no SDK atual.

Criação de bibliotecas

Como um desenvolvedor que criou bibliotecas .NET para uso público, você provavelmente esteve em situações em que precisa lançar novas atualizações. A forma como você realiza esse processo é muito importante, pois você precisa garantir que haja uma transição perfeita do código existente para a nova versão da sua biblioteca. Aqui estão várias coisas a considerar ao criar uma nova versão:

Controle de versão semântico

O versionamento semântico (SemVer) é uma convenção de nomenclatura aplicada a versões da sua biblioteca para significar eventos de marcos específicos. Idealmente, as informações de versão que você fornece à sua biblioteca devem ajudar os desenvolvedores a determinar a compatibilidade com seus projetos que usam versões mais antigas dessa mesma biblioteca.

A abordagem mais básica para o SemVer é o formato MAJOR.MINOR.PATCHde 3 componentes, onde:

  • MAJOR é incrementado quando você faz alterações de API incompatíveis
  • MINOR é incrementado quando você adiciona funcionalidade de maneira compatível com versões anteriores
  • PATCH é incrementado quando você faz correções de bugs compatíveis com versões anteriores

Há também maneiras de especificar outros cenários, por exemplo, versões de pré-lançamento, ao aplicar informações de versão à sua biblioteca .NET.

Compatibilidade com versões anteriores

À medida que você lança novas versões da sua biblioteca, a compatibilidade com versões anteriores provavelmente será uma das suas principais preocupações. Uma nova versão da sua biblioteca é compatível com uma versão anterior se o código que depende da versão anterior puder, quando recompilado, funcionar com a nova versão. Uma nova versão da sua biblioteca é compatível com binários se um aplicativo que dependia da versão antiga pode, sem recompilação, trabalhar com a nova versão.

Aqui estão algumas coisas a considerar ao tentar manter a compatibilidade com versões anteriores da sua biblioteca:

  • Métodos virtuais: Quando você torna um método virtual não virtual em sua nova versão, isso significa que os projetos que substituem esse método terão que ser atualizados. Esta é uma grande mudança de rutura e é fortemente desencorajada.
  • Assinaturas de método: quando a atualização de um comportamento de método requer que você altere sua assinatura também, você deve, em vez disso, criar uma sobrecarga para que a chamada de código para esse método ainda funcione. Você sempre pode manipular a assinatura do método antigo para chamar a nova assinatura do método para que a implementação permaneça consistente.
  • Atributo obsoleto: você pode usar esse atributo em seu código para especificar classes ou membros de classe que foram preteridos e provavelmente serão removidos em versões futuras. Isso garante que os desenvolvedores que utilizam sua biblioteca estejam mais bem preparados para alterações rápidas.
  • Argumentos de método opcionais: Quando você torna os argumentos de método opcionais anteriores obrigatórios ou altera seu valor padrão, todo o código que não fornece esses argumentos precisará ser atualizado.

Nota

Tornar os argumentos obrigatórios opcionais deve ter muito pouco efeito, especialmente se não mudar o comportamento do método.

Quanto mais fácil for para os usuários atualizarem para a nova versão da biblioteca, maior a probabilidade de que eles atualizem mais cedo.

Application Configuration File

Como desenvolvedor .NET, há uma grande chance de você ter encontrado o app.config arquivo presente na maioria dos tipos de projeto. Esse arquivo de configuração simples pode ajudar muito a melhorar a distribuição de novas atualizações. Você geralmente deve projetar suas bibliotecas de tal forma que as app.config informações que provavelmente mudarão regularmente sejam armazenadas no arquivo, desta forma, quando essas informações são atualizadas, o arquivo de configuração de versões mais antigas só precisa ser substituído pelo novo sem a necessidade de recompilação da biblioteca.

Consumindo bibliotecas

Como um desenvolvedor que consome bibliotecas .NET criadas por outros desenvolvedores, você provavelmente está ciente de que uma nova versão de uma biblioteca pode não ser totalmente compatível com seu projeto e muitas vezes você pode se ver tendo que atualizar seu código para trabalhar com essas alterações.

Para sua sorte, o C# e o ecossistema .NET vêm com recursos e técnicas que nos permitem atualizar facilmente nosso aplicativo para trabalhar com novas versões de bibliotecas que podem introduzir alterações significativas.

Redirecionamento de vinculação de montagem

Você pode usar o arquivo app.config para atualizar a versão de uma biblioteca que seu aplicativo usa. Ao adicionar o que é chamado de redirecionamento de vinculação, você pode usar a nova versão da biblioteca sem precisar recompilar seu aplicativo. O exemplo a seguir mostra como você atualizaria o arquivo app.config do seu aplicativo para usar a 1.0.1 versão do patch em ReferencedLibrary vez da versão com a 1.0.0 qual ele foi originalmente compilado.

<dependentAssembly>
    <assemblyIdentity name="ReferencedLibrary" publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />
    <bindingRedirect oldVersion="1.0.0" newVersion="1.0.1" />
</dependentAssembly>

Nota

Essa abordagem só funcionará se a nova versão do ReferencedLibrary for binária compatível com seu aplicativo. Consulte a seção Compatibilidade com versões anteriores acima para obter as alterações a serem observadas ao determinar a compatibilidade.

novo

Use o new modificador para ocultar membros herdados de uma classe base. Esta é uma maneira de classes derivadas podem responder a atualizações em classes base.

Veja o seguinte exemplo:

public class BaseClass
{
    public void MyMethod()
    {
        Console.WriteLine("A base method");
    }
}

public class DerivedClass : BaseClass
{
    public new void MyMethod()
    {
        Console.WriteLine("A derived method");
    }
}

public static void Main()
{
    BaseClass b = new BaseClass();
    DerivedClass d = new DerivedClass();

    b.MyMethod();
    d.MyMethod();
}

Saída

A base method
A derived method

A partir do exemplo acima, você pode ver como DerivedClass oculta o MyMethod método presente no BaseClass. Isso significa que, quando uma classe base na nova versão de uma biblioteca adiciona um membro que já existe em sua classe derivada, você pode simplesmente usar o new modificador em seu membro de classe derivado para ocultar o membro da classe base.

Quando nenhum new modificador é especificado, uma classe derivada ocultará por padrão membros conflitantes em uma classe base, embora um aviso do compilador seja gerado, o código ainda será compilado. Isso significa que simplesmente adicionar novos membros a uma classe existente torna essa nova versão da sua biblioteca tanto de origem quanto binária compatível com o código que depende dela.

Substituir

O override modificador significa que uma implementação derivada estende a implementação de um membro da classe base em vez de ocultá-lo. O membro da classe base precisa ter o virtual modificador aplicado a ele.

public class MyBaseClass
{
    public virtual string MethodOne()
    {
        return "Method One";
    }
}

public class MyDerivedClass : MyBaseClass
{
    public override string MethodOne()
    {
        return "Derived Method One";
    }
}

public static void Main()
{
    MyBaseClass b = new MyBaseClass();
    MyDerivedClass d = new MyDerivedClass();

    Console.WriteLine("Base Method One: {0}", b.MethodOne());
    Console.WriteLine("Derived Method One: {0}", d.MethodOne());
}

Saída

Base Method One: Method One
Derived Method One: Derived Method One

O override modificador é avaliado em tempo de compilação e o compilador lançará um erro se não encontrar um membro virtual para substituir.

Seu conhecimento das técnicas discutidas e sua compreensão das situações em que usá-las, ajudará muito a facilitar a transição entre as versões de uma biblioteca.