« Voltar
em .net c#

C#: Imitação ou inovação? - parte 3.

Na Parte 1 e na Parte 2, vimos como o C# se formou e evoluiu ao longo dos anos, desde seu início em 2000 até o C# 6.0 em 2015. Inicialmente, o C# nasceu como uma linguagem orientada a objetos estritamente tipada. Foi influenciado por Java e Delphi e chamado de imitação do Java por James Gosling, o criador da linguagem.

(Nota: esse post é uma tradução de uma série de 3 posts do blog do Michael Shpilt. Ele me autorizou a fazer a tradução :) Alguns termos são difíceis de traduzir ou não tem tradução, portanto deixei em inglês mesmo.)

Em versões posteriores, o C# evoluiu e adotou alguns atributos de programação funcional com o Lambda Expressions, LINQ e Extension Methods. C# ganho muita popularidade com esses recursos. Após a versão 3.0 em 2008, ela se tornou a sétime linguagem mais popular de acordo com o índice TIOBE.

C# imitou fluxos de trabalho assíncronos do F# e criou o recurso async/await. Também melhorou a interoperabilidade COM, alcançando o Visual Basic .NET, que era o rei do COM até aquele momento. Depois de C# 4.0 em 2010, sua popularidade aumentou ainda mais e ela se tornou a quarta linguegem mais popular do mundo.

Vimos que, embora o C # tenha imitado a maioria de seus recursos de linguagens existentes (como seria de se esperar de uma linguagem de programação), ela o faz muito bem, muitas vezes melhor que o inovador original. C# teve algumas inovações incríveis, como o LINQ por exemplo.

Neste artigo, veremos o C# evoluindo para uma área completamente diferente das versões anteriores e tentando se tornar a única linguagem que governa todas elas.

C# 7.0

A versão 7.0 foi lançada em Março de 2017. A essa altura, o Roslyn estava vivo e forte, o que permitia ao time do C# lançar muitas funcionalidades pequenas de forma rápida.

Tuplas – Tuplas já existiam antes dessa versão, mas não tinham suporte na linguagem. Cada item era referenciado como Item1, Item2, etc. A versão 7.0 adicionou uma sintaxe muito conveniente para suportar tuplas:

(string Alpha, string Beta) namedLetters = ("a", "b");
private static (int celsius, int fahrenheit) GetTemperature()  
{
    return (c, f);
}

Tuplas já existiam em Python e Scala. Imitação.

Variáveis out – A partir dessa versão podemos declarar variávels out direto no método:

// antes
int number1;  
int.TryParse(input, out number1))

// agora
int.TryParse(input, out int number2))  

Uma adição muito bem-vinda à linguagem.
O parâmetro out é único ao C#, portanto inovação.

Discards – a possibilidade de usar o caracter "_" para variáveis que você não vai precisar depois. Útil quando vai desconstruir tuplas e com parâmetros out.

//o método devolve váris infos, mas só nos importamos com bithday e adress
var (_, _, birthday, _, address) = GetCurrentUserDetails();  

Já existia em Python, Scala e existem funcionalidades parecidas no Perl. Imitação.

Pattern Matching – O código fala por si:

if (shape is Square square)  return square.Side * square.Side;

// usado com switch
switch (shape)  
{
    case Square square:
        return square.Side * square.Side;

// usado com switch e when
switch (shape)  
{
    case Square sqr when sqr.Side == 0:

Fiquei muito feliz quando essa saiu.
Por mais que seja inovador, Scala e Kotlin já tem uma funcionalidade parecida. Imitação.

ref locals and returns – Permite referências a váriaveis, muito parecido com C:

int[,] matrix = ...  
...
ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);  
//assumindo que os valores encontrados são (4,2)
item = 123;  
Console.WriteLine(matrix[4, 2]); //prints '123'  

Como eu disse, isso é muito parecido com ponteiros em C. A inovação é que C# é uma linguagem com garbage colector onde buffers de memória podem se mover. Inovação.

Funções locais – mesclar funções dentro de outras funções para limitar seu escopo e visibilidade:

public static void ShowXTimes (string str, int x)  
{
    for (int i = 0; i < x; i++)
    {
       Show(x);
    }

    void Show()
    {
       Console.WriteLine(str);
    }
}

Já existiam no Python e Javascript. Imitação.

Mais expression-bodied members – Uma nova síntaxe para métodos e propriedades:

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

private string label;  
// Expression-bodied get / set accessors.
public string Label  
{
    get => label;
    set => this.label = value ?? "Default label";
}

Essa não é bem uma nova funcionalidade, apenas uma síntaxe nova para uma funcionalidade já existente.

Throw Expressions – A possibilidade de lançar exceções em expressões condicionais.

private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ??  
throw new InvalidOperationException("Could not load config");  

No F# temos o if-then-else que funciona como uma expressão. Dá pra fazer algo assim:

let res = if (y = 0) then failwith "Divisor cannot be zero." else x / y  

Portanto, imitação.

Melhorias em síntaxe de números literais – Prefixo 0b para números binários , e _ para separar números grandes:

public const int Sixteen = 0b0001_0000;  

Ambas funcionalidades já existiam no Python. Imitação.

C# 7.1

Em Agosto de 2017 foi lançada a versão 7.1: foi a primeira versão que não é um número inteiro. O time de desenvolvimento parece ter decidido lançar versões com menos funcionalidades, mas mais rápido.

Nessa versão se tornou possível configurar o compilador para usar uma versão específica.

Método main assíncrono – A possibilidade do ponto de entrada da aplicação ter o modificador async. Inovação.
Essa funcionalidade foi posteriormente imitada por Python e Kotlin.

Expressões literais default – a possibilidade de usar expressões literais default com a palavra default quando o tipo pode ser inferido.

//antes do C# 7.1
int x = default(int);  
//com c# 7.1
int x = default;  

Mais açúcar sintático do que funcionalidade, sem veredito nessa.

Nomes inferidos a elementos de tuplas – Os nomes dos elementos de uma tupla podem ser inferidos a partir da inicialização da tupla.

int count = 5;  
string label = "Colors used in the map";  
// antes do c# 7.1
var pair = (count: count, label: label);  
// com c# 7.1
var pair = (count, label);  

Mais uma vez, açucar sintático. Há uma sintaxe simlar no Javascript ES2015.

C# 7.2

Lançada em Novembro de 2017, apenas 3 meses depois da última versão.

Span and Memory – guarda um ponteiro para uma parte de um array ou o array completo. Então se você tem um array de bytes de 1 a 50, então você pode ter um Span que aponta para o intervalo 10...20.
Python já tem uma notação bem parecida e mais poderosa. Imitação.

Técnicas para escrever código seguro e eficiente – Uma combinação de melhorias de síntaxe que permitem trabalhar com tipos por valor usando semântica de tipos por referência:
-O modificador in em parâmetros para especificar que um argumento é passado por referência mas não é modificado pelo método chamado. -O modificador ref readonly em retornos de método, para indicar que um método retorna seu valor por referência mas não permite alterações naquele objeto. -A declaração readonly struct, pra indicar que uma struct é imutável e deve ser passada como um parâmetro in para seus métodos membros. Essas 3 funcionalidades parecem ter sido inspiradas pelos vários modificadores const do C++. Imitação.
-A declaração ref struct, pra indicar que uma struct acessa memória gerenciada diretamente e deve sempre ser alocada na stack. Inovação.

Non-trailing named arguments – Parâmetros nomeados podem ser seguidos de parâmetros posicionais.

PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");  
// 31 tá sendo passado sem um nome

Posso estar errado, mas me parece que C# é a única linguagem com essa funcionalidade. Inovação.

Modificador de acesso private protected – esse modificador permite acesso para classes derivadas em um mesmo assembly. Java tinha esse modificador também (agora obsoleto) na versão 1.0, que foi removido na JDK 1.0.2 (a primeira versão estável). Esse modificador foi definido da seguinte maneira no Java:*
O significado de private protected era de limitar a visibilidade estritamente a subclasses (e remover o acesso a pacotes).*
Eu li isso 10x mas não entendi se é a mesma coisa. Já que de qualquer forma nunca foi lançado no Java, considerarei que é uma inovação do C#.

C# 7.3

Essa versão realmente focou em melhorar a performance de código gerenciado. Todo o conceito de código unsafe em um ambiente garbage collected é uma inovação do C#, e não existe em nenhum outro ambiente que usa garbage collection. Então não há razão para determinar se o seguinte é uma inovação ou imitação já que é tudo único do C#.

Access fixed fields without pinning – a possibilidade de acessar o índice de atributos fixados sem um ponteiro adicional.
Uso de initializers em arrays stackalloc – uma melhoria de síntaxe bacana para o já existente stackalloc.
Você pode usar declarações fixadas com qualquer tipo que suporte um padrão.
Você pode usar constrains genéricas adicionais.

As seguintes melhorias foram feitas a funcionalidades adicionais:
Testes usando == e != com tuplas - já existia em Python. Imitação.

Uso de variáveis de expressão em mais lugares - relevante à parâmetros out, que são únicos do C#.
Essa funcionalidade de tá a possibilidade de definir o nome dos atributos das auto-implemented properties. Exemplo:

[field: SomeThingAboutFieldAttribute]
public int SomeProperty { get; set; }  

Não vou chamar de inovação exatamente por que não há outra linguagem que tem atributos e propriedades juntos. Java já suporta anotações para métodos e variáveis.

Parece que as versões 7.x do C# são relacionadas a melhoria de codigo não seguro, ponteiros e gerenciamento de memória em baixo nível. Em outras palavras, a linguagem se torna eficiente para algoritmos como C e C++.

Essa é uma meta bastante ousada, mas, teoricamente, isso pode ser alcançado em determinados blocos de código algorítmicos de gargalo. O # pode efetivamente desabilitar o garbage collection fixando variáveis ​​e usar os novos recursos ref e stackalloc para trabalhar na stack com ponteiros, da mesma forma que o código C ++ nativo.

Com a versão 7.3, cobrimos todas as versões do C # até hoje. Agora é hora de ver o que acontecerá no futuro. Mas primeiro, vamos ver como o C # está se saindo em termos de popularidade em 2018.

Popularidade da linguagem

Nos últimos anos, o C# não tem sido muito usado em startups.

Esse papel é preenchido principalmente por Java, JavaScript, Ruby e Python. No entanto, a linguagem continua a ser extremamente popular no mercado. O levantamento do StackOverflow de 2018 coloca o C# como a 4ª linguagem de programação mais popular (3ª se não considerarmos SQL). O .NET Core é o terceiro framework mais popular depois do Node.js e do AngularJS.

O TIOBE coloca o C# em sexto lugar, depois de Visual Basic .NET (sim, pois é). O índice PYPL coloca em quarto lugar, depois de Python, Java e Javascript.

C# 8.0

Estamos chegando muito perto do lançamento do C# 8.0, que teoricamente vai sair com o Visual Studio 2019.

Mads Torgersen, o Program Manager do C#, escreveu recentemente sobre todos os novos recursos do C# 8. Vamos analisá-los e ver quais são as inovações e quais são as imitações:

Tipos por referência nuláveis – todos os tipos por referência, nuláveis por padrão, agora irão mostrar um warning quando atribuirmos null:

string s = null; // Warning: Assignment of null to non-nullable reference type  
string? s = null; // Ok  

Com isso, a Microsoft finalmente está lidando com o erro de um bilhão de dólares.
Para não quebrar código já existente, essa funcionalidade é configurável.
Typescript já implementou uma funcionalidade similar com tipos estritamente nuláveis. Imitação.

Streams async – permite usar await foreach em métodos async com yield return:

async IAsyncEnumerable<int> GetBigResultsAsync()  
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}

Imagino que seja como BlockingCollection’s GetConsumingEnumerable para métodos async? for async methods? Terei que perguntar pro Mads, mas se entendi dirento é uma inovação.

Ranges and Indices – adição do tipo Index, que pode funcionar como um índice em arrays:

Index i1 = 3;  // number 3 from beginning  
Index i2 = ^4; // number 4 from end  
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"  

E uma nova síntaxe para intervalos, que ainda não foi decicido mas provavelmente será Span:

var slice = a[i1..i2]; // { 3, 4, 5 }  

Ambas funcionalidades já existem em Python com índices negativos e a notação slice.

Implementação padrão dos membros da interface - Como nas classes abstratas, uma interface pode fornecer uma implementação padrão que a classe implementadora pode optar por substituir ou não. Depois de muito tempo, o C# imita um recurso que apareceu pela primeira vez em Java. Imitação.
Isso levanta questões sobre a diferença entre classes abstratas e interfaces e também sobre problemas com herança múltipla. Este artigo Java refere-se a algumas dessas questões e também é relevante para o C#.

Patterns recursivos – Um progresso interessante quando falamos de pattern matching:

IEnumerable<string> GetEnrollees()  
{
    foreach (var p in People)
    {
        if (p is Student { Graduated: false, Name: string name }) yield return name;
    }
}

Nesse exemplo, se p é Student e p.Graduated for false e Name não for nulo, então Name é retornado.
Se você for como eu, provavelmnte vai demorar um pouco pra entender essa síntaxe. Na minha opinião, é mais intuitivo escrever:

if (p is Student st && !st.Graduated && st.Name!= null) yield return name;  

Esses patterns recursivos já existem em Rust e Erlang. Imitação.

Switch expressions – uma nova síntaxe pra pattern matching em switch:

var area = figure switch  
{
    Line _      => 0,
    Rectangle r => r.Width * r.Height,
    Circle c    => Math.PI * c.Radius * c.Radius,
    _           => throw new UnknownFigureException(figure)
};

Muito parecido com a síntaxe de pattern matching do Kotlin. Imitação.

Target-typed new-expressions – quando o tipo de um objeto pode ser derivado de uma expressão, é permitido que ele seja omitido:

Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; // all Points  

Uma inovação.

Conclusão

Este último artigo resume todos os recursos do C# a partir do primeiro lançamento da versão em 2002 até C# 8. Também vimos como C# ganhou força ao longo do caminho, permanecendo consistentemente uma das línguas mais populares do mundo.

É incrível todas mudanças que o C # tomou durante todos esses anos. Acho que se tornou uma das únicas linguagens verdadeiramente multifuncionais existentes. Considere os seguintes paradigmas que existem em paralelo na linguagem: -É orientado a objeto
-É funcional (LINQ, métodos de extensão)
-É gerenciada (garbage collected)
-Utiliza ponteiros e códigos inseguros como uma linguagem não gerenciada
-É estaticamente tipada
-Pode ser dinâmica (com a palavra-chave dinamyc)

Dos comentários que recebi, esses artigos foram um pouco controversos. Algumas pessoas pensaram que eu estava tentando ser crítico, e não havia sentido nisso. Meu ponto não era julgar. Em vez disso, eu queria explorar a história da linguagem C # e como ela se desenvolveu e evoluiu ao longo dos anos no contexto de outras linguagens.

De qualquer forma, eu espero que você tenha algum benefício da série, talvez descobrindo alguns recursos C# que não conhece (eu tenho certeza que sim). Eu adoraria ouvir seus comentários nos comentários abaixo e se inscrever no blog para ser notificado sobre novos artigos.


Inscreva-se no blog do Michael e confira outros artigos dele!

Quer ficar em dia com os meus posts e novidades?
Participe do
inscreva na minha newsletter, me segue no Twitter e na minha página!

comments powered by Disqus