8 recursos incríveis de C # que você deve conhecer

C # e . NET estiveram em desenvolvimento ativo consistente nas últimas duas décadas; a cada ano, a linguagem recebe um conjunto de novos recursos úteis. Discutiremos alguns de nossos favoritos que achamos que os iniciantes deveriam conhecer.
Tipos de referência anuláveis
C # teve tipos de valor anuláveis por um tempo, como ” int? ” que pode conter um int ou o valor nulo, em oposição a um int tradicional que sempre teria um valor padrão zero. Eles são úteis para muitas coisas, incluindo classes destinadas à desserialização JSON, onde nem todos os campos podem estar presentes.
No entanto, os tipos de referência sempre puderam receber um valor nulo, então qual é o objetivo desse novo recurso do C # 8.0?
Tipos de referência anuláveis basicamente impõem uma distinção entre variáveis de referência que podem ser nulas e variáveis de referência que não podem. É um recurso inovador que provavelmente deixará sua base de código com muitos avisos, portanto, é algo que você deve ativar manualmente. Assim que estiver ligado, o compilador começará a dizer a diferença entre:
- string ?, que pode ser nula e retém o “ padrão ” comportamento de versões anteriores e
- string, que não pode ser nula. Nunca pode ser nulo, porque deve receber um valor padrão e nunca pode ser definido como nulo.
Isso tem muitos efeitos interessantes, mas o mais importante é um foco implícito no tratamento adequado de valores nulos em um nível de linguagem. Por exemplo, O compilador gritará com você se você tentar retornar um valor nulo para uma função que retorna um valor não anulável. Ele também gritará com você se você tentar passar um valor possivelmente nulo para uma função que não está esperando por isso.
Embora isso possa soar restritivo, assim como a digitação estática, ele leva a um código melhor e mais utilizável no final. É altamente recomendável ativá-lo para a maioria dos projetos, e você pode ler nosso guia completo sobre eles para saber mais.
Para ligá-lo, você precisará editar o arquivo do seu projeto. No Visual Studio, clique com o botão direito do mouse em seu projeto e clique em “ Editar arquivo de projeto. ” Em seguida, ative-o com a seguinte diretiva:
< Anulável > ativar < / Anulável >
Se você estiver usando o formato de projeto legado, pode ser necessário substituí-lo manualmente por uma diretiva na parte superior de cada arquivo:
#nullable enable
RELACIONADO: Como funcionam os tipos de referência anuláveis do C #?
Operadores de Null-Conditional e Null Coalescing
Em vez de ter que verificar se (algo == null), C # tem uma abreviatura impressionante com operadores de acesso de membro condicional nulo. Basicamente, em vez de usar um ponto para acessar algo que pode ser nulo, você pode usar um ponto de interrogação e um ponto, que executará a verificação de nulo automaticamente.
Você também pode usá-los para chamar métodos em objetos nulos ou acessar índices em arrays nulos. Se o objeto acabar sendo nulo, ele simplesmente não fará nada e retornará um valor nulo.
referência?. referência de campo? . method (); referência? [N]
Observe que o último não impede um IndexOutOfRangeException — ele simplesmente acessa o enésimo elemento de uma lista possivelmente nula.
No entanto, você pode ter que trabalhar com os valores nulos retornados por esta expressão e, para tornar isso mais fácil, C # tem operadores de coalescência de nulos. Eles podem ser usados para atribuir um valor alternativo no caso de uma expressão (qualquer expressão) retornar um valor nulo. Basicamente, eles são valores de backup. Você pode especificá-los com pontos de interrogação duplos:
string value = GetValue () ?? "Backup"
Há também o operador ?? =, que funciona como || na medida em que não avaliará o valor de backup se o primeiro valor retornar um resultado adequado.
Tuplas
Sempre quis retornar vários valores de um método? Com tuplas você pode, e o C # moderno tem grande suporte de linguagem para elas desde o C # 7.0. Basta retornar dois valores entre parênteses e separados por vírgulas, e você pode acessar os itens individuais dentro deles.

Embora não seja obrigatório, é prática comum fornecer esses nomes, como (flutuante X, flutuante Y, flutuante Z), em vez de acessá-los por números de item.
Você também pode usar a desconstrução da tupla para separar uma tupla em várias variáveis de componente.

Isso é bastante útil para construtores simples, onde você precisa definir alguns campos iguais aos argumentos de entrada. Usar a desconstrução de tupla faz isso de maneira bastante limpa:

Sobrecarga do construtor com: this ()
Construtores, como qualquer outro método, podem ser sobrecarregados de operador para oferecer suporte a muitas combinações diferentes de parâmetros. No entanto, como os construtores são comumente usados para inicializar muitos campos, isso pode levar à duplicação do código.
Uma solução rápida e suja seria compartilhar uma “ classe de inicialização ” método que é chamado de todos os métodos de construtor sobrecarregados, mas se você tiver tipos de referência anuláveis ativados, você receberá avisos de nulidade para campos não anuláveis que estão realmente sendo definidos, já que o compilador não é &’ t inteligente o suficiente para entender a inicialização em chamadas de função impuras.

Mas, há uma solução para isso, e é um pouco estranho. Vem da herança do construtor, que é outro recurso incrível que permite expandir o construtor da classe base. Ele usa a mesma sintaxe de herança, dois pontos, seguido por base (parâmetros). Isso irá chamar automaticamente o construtor base (antes do novo). Observe que você ainda precisa colocar os parâmetros do construtor de base na definição do método.

A parte legal é que você não precisa usar o básico; você pode fazer exatamente a mesma coisa com: this (), que chamará um construtor dentro da própria classe. Você pode usar isso para especificar parâmetros adicionais sem copiar o código de inicialização.

Obviamente, você precisará declarar os campos opcionais como anuláveis, uma vez que eles são como o construtor base não os suporta. Mas isso é intencional aqui; neste exemplo, o nome e o sobrenome da pessoa precisam ser definidos, mas o e-mail pode ou não ser, tornando-o adequado para um tipo anulável.
Construtores estáticos
Construtores são comumente usados para criar instâncias de classes usando a nova palavra-chave. No construtor, você pode configurar os campos necessários para inicializar a classe.
Mas e as classes estáticas? Bem, eles também podem usar construtores. Na verdade, as classes regulares podem usar construtores estáticos para configurar suas propriedades estáticas.
No entanto, eles não funcionam exatamente na inicialização. Embora o exemplo acima pareça correto, definindo o startupTime em um construtor estático, não é garantido em tempo de execução porque C #, e o MSIL em que é executado, é uma linguagem compilada Just-In-Time.
A compilação JIT ocorre, bem, na hora certa, exatamente quando a classe é necessária. Isso significa que a classe ficará sentada em seu canto da assembléia, juntando poeira até que um de seus campos ou métodos seja necessário. Assim que for necessário, o tempo de execução do . NET o removerá, compilará e só então chamará o construtor estático.
No entanto, o construtor estático ainda é executado antes de qualquer coisa, mesmo antes de os campos estáticos serem configurados e antes de qualquer coisa poder ser referenciada. Eles ainda são muito úteis quando você precisa deles. Como alternativa, você pode chamar um método de inicialização a partir da rotina de inicialização de seu aplicativo se precisar executar algo em ordem cronológica.
Parâmetros de tipo genérico
Você definitivamente já encontrou isso antes, embora possa não ter escrito um. Os parâmetros de tipo genérico permitem que você escreva funções que são independentes de tipo e não se importam com o tipo que está sendo passado a elas. O principal exemplo disso são as coleções; uma Lista < string > e uma Lista < int > usam o mesmo código, mas recebem um parâmetro de tipo genérico diferente.
Os genéricos são muito fáceis de usar por conta própria. Basta adicionar um nome para a variável de tipo entre colchetes na definição de classe ou método. É prática comum usar T, ou pelo menos nomes que começam com T. Por exemplo, um dicionário pode ter TKey e TValue, dois tipos diferentes.

Você também pode usá-los em funções e, se usar o parâmetro de tipo como um tipo de argumento, ele pode até ser inferido automaticamente.

Os tipos genéricos criam vários “ tipos ” da classe genérica. Isso significa que os campos estáticos serão separados com base no tipo da classe, então List < string > não compartilha dados com List < int & gt ;.
Por causa disso, você precisa passar um parâmetro de tipo se quiser fazer referência ao nome da classe diretamente. Isso pode ser um problema em alguns casos, então, alternativamente, se você precisar oferecer suporte a vários tipos, pode lançar de e para o objeto usando uma técnica chamada boxe.
Delegados
Delegados são uma forma de empacotar métodos em variáveis. Isso é útil para injeção de dependência, que é um nome excessivamente extravagante para um conceito simples — classes flexíveis devem obter alguns valores, chamados de dependências, de suas variáveis de construtor, permitindo que o usuário dessa classe especifique as dependências à vontade.
Os delegados permitem que você faça isso com funções. Você pode fazer com que a classe execute qualquer tipo de ação e ela não se preocupa com a implementação. Eles ainda são digitados estaticamente — você terá que definir os parâmetros de entrada e saída como faria com qualquer função, exceto marcá-la com “ delegado ” e não escreva um corpo.

Você pode então atribuir uma função a essa variável e, em seguida, usar a variável para invocar a função. Você pode fazer isso diretamente como mostrado, ou você pode usar myDelegate. Invoke (), que faz a mesma coisa, mas mais detalhado.

Você pode ler mais sobre delegados em nosso guia para usá-los.
Indexadores
As classes em C # usam campos para armazenar dados e propriedades para expor esses dados a outras classes. Propriedades são realmente apenas um método que expõe um campo para que você possa acessá-lo fazendo class. property.
Você pode fazer o mesmo para indexação, por exemplo classe [índice]. Isso pode ser usado para criar um comportamento de indexação personalizado. Por exemplo, você pode criar uma lista 2D a partir de uma Lista 1D < T > criando um indexador personalizado que retorna um valor com base nos argumentos de entrada.

Nenhum comentário