C# 8.0 - As novidades da nova versão
Hoje vou replicar informações da Microsoft referente a algumas das novidades que a versão 8.0 da linguagem C# vai oferecer. |
A versão final
do C# 8.0 esta
prestes a sair do forno , provavelmente junto com a .NET Core
3.0. Enquanto isso
vamos dar uma espiada nas novidades que esta nova versão vai trazer.
Lembrando que estamos atualmente no VS 2019, mas já temos a versão preview.
A seguir vou apresentar um resumo de 5 novidades do C# 8.0.
Você precisa instalar o VS 2019 e o .NET Core SDK 3.0 Preview na sua máquina para testar esses recursos do C# 8.0.
Após isso abra o VS 2019 e em Ferramentas -> Opções -> Projetos e Soluções marque a opção para usar a versão prévia do SDK .NET Core.
Agora você vai poder selecionar a versão .NET Core 3.0 na opção Aplicativo da janela de propriedades da Solução.
E na opção Compilar da janela de propriedades do projeto, vai ter que selecionar a versão do idioma como C# 8.0(beta):
Após essas configurações você pode abrir o VS 2019 e testar o C# 8.0.
1- Nullable reference types
O objetivo deste recurso e prevenir as exceções de null reference relativa a referência nula.
Na nova versão será emitido um aviso ou alerta quando um null for atribuido a um tipo de referência como uma string. Se você desejar atribuir o valor null mesmo assim pode usar um tipo de referência anulável ou nullable reference type:
string nome = null; Warning: Assignment of null to non-nullable reference type
string? nome = null;
OK |
Nota: Dentro de um contexto de anotação anulável, qualquer variável de um tipo de referência é considerada como um tipo de referência não anulável. Se você quiser indicar que uma variável pode ser nula, você deve acrescentar o nome do tipo com o caractere ? para declarar a variável como um tipo de referência anulável.
Quando você for tentar usar uma referência anulável, você precisa verificar primeiro a referêcia nula.
O compilador analisa o fluxo do seu código para ver se um valor nulo pode vir a ser executado:
void MetodoTeste(string? nome)
{
Console.WriteLine(nome.Length); // Warning: Possível exceção e referência nula
if (nome != null)
{
Console.WriteLine(nome.Length); // Ok: Se nome for null esse código não executa
}
}
|
Dessa forma o recurso agora permite que você expresse a sua intenção de referência nula, e, ele vai emitir um aviso quando você desobedecer a regra.
2- Switch Expressions
Uma instrução switch produz um valor em cada um dos seus blocos case.
O novo recurso switch expressions permitem que você use uma sintaxe de expressão mais concisa. Existem menos palavras-chave case e break reptidas e menos chaves. O código fica mais enxuto.
Se você já conhece a instrução switch pode dizer o que o seguinte trecho de código esta fazendo:
public enum Rainbow
{
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
}
public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),
Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),
Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
_ => throw new ArgumentException(message: "Valor inválido", paramName: nameof(colorBand)),
};
|
Mas espere, aqui não estamos usando case, break ou return. Nas switch expressions essas palavras chaves não são mais necessárias.
Existem várias melhorias de sintaxe ao usar uma switch expressions :
Compare com o código abaixo onde estamos usando a instrução switch:
public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
switch (colorBand)
{
case Rainbow.Red:
return new RGBColor(0xFF, 0x00, 0x00);
case Rainbow.Orange:
return new RGBColor(0xFF, 0x7F, 0x00);
case Rainbow.Yellow:
return new RGBColor(0xFF, 0xFF, 0x00);
case Rainbow.Green:
return new RGBColor(0x00, 0xFF, 0x00);
case Rainbow.Blue:
return new RGBColor(0x00, 0x00, 0xFF);
case Rainbow.Indigo:
return new RGBColor(0x4B, 0x00, 0x82);
case Rainbow.Violet:
return new RGBColor(0x94, 0x00, 0xD3);
default:
throw new ArgumentException(message: "Valor inválido", paramName: nameof(colorBand));
};
}
|
3- Asycnchronous streams
A partir do C# 8.0, você pode criar e consumir streams de forma assíncrona. Um método que retorna um stream assíncrono possui três propriedades:
Consumir um stream assíncrono requer que você adicione a palavra-chave await antes da palavra-chave foreach ao enumerar os elementos do fluxo, logo o método deve usar async e retornar um tipo permitido para um método assíncrono.
Normalmente, isso significa retornar um Task ou Task<TResult>. Também pode ser um ValueTask ou ValueTask <TResult>.
Um método pode consumir e produzir um stream assíncrono, o que significa que retornaria um IAsyncEnumerable<T>.
No exemplo a seguir geramos uma sequência de 0 a 19, esperando 100 ms entre a geração de cada número:
public static async System.Collections.Generic.IAsyncEnumerable<int> GeraSequencia()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(100);
yield return i;
}
}
|
Para enumerar a sequência você deve usar a instrução await foreach:
await foreach (var numero in GeraSequencia())
{
Console.WriteLine(numero);
}
|
4- Indices e ranges
Ranges
(Intervalos) e índices fornecem uma sintaxe suscinta para especificar
sub-intervalos em uma matriz, Span <T> ou ReadOnlySpan<T>.
Você pode especificar um índice a partir do final onde você define a partir
do final usando o operador ^.
Se você está familiarizado com a notação array[2] que significa o elemento "2 desde o início".
Agora, a notação array[^2] significa o elemento "2 a partir do final".
O índice '^0' significa "o fim" ou o índice que segue o último elemento.
Você pode especificar um intervalo com o operador de intervalo: ...
Por exemplo, 0 ..^0 especifica o intervalo inteiro do array: 0 do início até, mas não incluindo 0 do final.
Cada operando pode usar "a partir do inicio" ou "a partir do final".
Além disso, qualquer operando pode ser omitido. Os padrões são 0 para o índice inicial e ^0 para o índice final.
Vejamos um exemplo onde temos uma matriz anotada com seu índice a partir do inicio e partir do final:
var palavras = new string[]
{
// indice do inicio índice do final
"Um", // 0 ^9
"dia", // 1 ^8
"após", // 2 ^7
"outro", // 3 ^6
"dia", // 4 ^5
"faz", // 5 ^4
"a", // 6 ^3
"grande", // 7 ^2
"diferença" // 8 ^1
};
|
O índice de
cada elemento reforça o conceito de "desde o início"
e "do final", e os intervalos são exclusivos do
final do intervalo. O "começo" de todo o array é o
primeiro elemento. O "final" de todo o array está
além do último elemento.
Você pode recuperar a última palavra usando o índice ^1:
Console.WriteLine($"A última palavra é {palavras[^1]}");
|
O código a seguir cria um subintervalo com as palavras "dia", "após" e "outro" e inclui o elemento palavras[1] até palavras[3].
var minhasPalavras = palavras[1..4];
foreach(var p in minhasPalavras)
{
Console.WriteLine(p);
}
|
Abaixo temos outros exemplos de utilização de Range:
WriteLine($"A última palavra é : {palavras[^1]}");
WriteLine($"A primeira palavra é : {palavras[^9]}");
Range intervalo = 1..9;
foreach (var p in palavras[intervalo])
{
Console.WriteLine(p);
}
ReadLine();
|
5- Declaração Using
Uma declaração using é uma declaração de variável precedida pela palavra chave using. Diz ao compilador que a variável que está sendo declarada deve ser descartada no final do escopo.
Considere o seguinte código que grava um arquivo de texto:
static void EscreverNoArquivo(IEnumerable<string> linhas)
{
using var arquivo = new System.IO.StreamWriter("Texto.txt");
foreach (string linha in linhas)
{
// se linha não contém 'teste' escreve a linha no arquivo
if (!linha.Contains("teste"))
{
arquivo.WriteLine(linha);
}
}//o arquivo é liberado aqui
}
|
No exemplo anterior, o arquivo é descartado quando a chave de fechamento do método é atingida. Esse é o fim do escopo no qual o arquivo é declarado.
O código anterior é equivalente ao seguinte código usando a declaração clássica de instruções:
static void EscreverNoArquivo(IEnumerable<string> linhas)
{
using (var arquivo = new System.IO.StreamWriter("Texto.txt"))
{
foreach (string linha in linhas)
{
// se linha não contém 'teste' escreve a linha no arquivo
if (!linha.Contains("teste"))
{
arquivo.WriteLine(linha);
}
}
} // arquivo é liberado aqui
}
|
No exemplo
anterior, o arquivo é descartado quando a chave de fechamento
associada à instrução using é atingida.
Em ambos os casos, o compilador gera a chamada para
Dispose(). O compilador gera um erro se a expressão na instrução
using não for descartável.
Para tornar mais claro vejamos o exemplo abaixo onde primeiro estamos usando a sintaxe clássica da instrução using:
static void Main(string[] args)
{
using (var arquivo = new FileStream("texto.txt", FileMode.Open))
using (var leitor = new StreamReader(arquivo))
{
var texto = leitor.ReadToEnd();
// faz alguma coisa
}
}
|
Vamos agora aplicar o novo recurso usando as declarações using :
static void Main(string[] args)
{
using Stream arquivo = new FileStream("texto.txt", FileMode.Open);
using StreamReader leitor = new StreamReader(arquivo);
var texto = leitor.ReadToEnd();
// faz alguma coisa
}
|
Temos o código com o mesmo comportamento mas agora mais suscinto e enxuto.
Em outro artigo vamos continuar a apresentar os novos recursos do C# 8.0.
"Sujeitai-vos, pois, a Deus, resisti ao diabo, e ele fugirá de vós." Tiago 4:7
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? |
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a Objetos com VB .NET
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8