C# 13: Domine Tipos e Otimize o Desempenho do Código

Neste tutorial, você aprenderá como fazer escolhas adequadas de tipos para maximizar o desempenho em aplicações C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET.. Abordaremos desde o uso inteligente de valores e referências até recursos como Span<T>, structs e classesBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC., explorando também boas práticas para reduzir alocações desnecessárias e, assim, otimizar a velocidade de execução.

Entendendo a Importância de Escolher o Tipo Correto🔗

Cada tipo em C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET. possui características de alocação de memória, transferência de dados e tratamento em tempo de execução que influenciam diretamente a performance. Quando falamos em tipos por valorBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. (Value Types), estamos lidando com dados que, em geral, são armazenados na stack (pilha). Já os tipos por referência (Reference TypesBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC.) ficam no heap, exigindo acesso indireto por ponteiros internos.

Por que isso importa? Porque toda chamada de método, uso de coleções e até loops de alto volume podem gerar um impacto significativo, acumulado ao longo de milhares ou milhões de iterações.

Struct vs. Class: Quando Usar Cada Tipo🔗

Um debate clássico em C#: structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. ou classBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC.? Em C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET., as regras básicas continuam:

Uma novidade que facilita a decisão é o maior suporte a tipos record structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. e record classBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC., permitindo criar tipos mais otimizados e com benefícios de boilerplate reduzido, como comparações estruturais (“por valor semântica”) e deconstruction. Entretanto, quando se fala apenas em desempenho, considere se realmente precisa de todos os recursos de uma classBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. antes de optar por ela.

Usando Span<T> para Evitar Cópias Desnecessárias🔗

O Span<T> permite manipular blocos de memória contíguos sem criar novas alocações. Em vez de copiar arrays ou strings durante operações de slicing e leitura, você trabalha diretamente sobre a mesma região de memória.

Por exemplo, considere este trecho de código para acessar um intervalo de um array:

int[] numeros = { 10, 20, 30, 40, 50 };
// Abordagem sem Span<T>
// Cria um novo array com elementos de índice 1 a 3
int[] subNumeros = numeros[1..4];
// Com Span<T>, reaproveita memória
Span<int> spanNumeros = new Span<int>(numeros);
Span<int> slice = spanNumeros.Slice(1, 3);
// Agora 'slice[0]' corresponde diretamente ao 'numeros[1]'

Benefícios:

  • Evita alocações extras e cópias de dados.
  • Torna o código mais fluido para manipular fatias de arrays e strings.
  • Integra-se muito bem a cenários de alto desempenho, como processamentos de stream ou buffers de rede.

Classes Seladas (sealed) para Evitar Sobrecarga de Herança🔗

Mesmo sendo um recurso já existente antes do C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET., o uso de sealed classBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. continua relevante quando a intenção é bloquear herança e otimizar chamadas de método. O tempo de execução do .NET (CLR) pode efetuar diversas otimizações em classesBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. seladas, pois sabe que não ocorrerá polimorfismo dinâmico.

public sealed class Calculadora
{
    public int Somar(int a, int b) => a + b;
}

Por que selar a classe melhora desempenho?

ValueTask Versus Task: Quando Optar pelo ValueTask🔗

Para operações assíncronas que podem terminar de forma imediata ou ter pouco trabalho a ser feito, ValueTask é útil. Diferentemente de Task, que é sempre alocado no heap, um ValueTask pode funcionar como uma structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. simples e evitar uma alocação adicional caso o resultado já esteja disponível:

// Exemplo de método que se beneficia de ValueTask
public async ValueTask<int> CalcularAlgoAsync(int x)
{
    if (x < 10)
    {
        // Retorna com computação imediata, sem criar uma Task no heap
        return x + 10;
    }
    // Caso contrário, processa de forma assíncrona
    await Task.Delay(100);
    return x + 100;
}

Quando usar?

  • Em métodos muito simples, chamados com alta frequência.
  • Em bibliotecas de baixo nível onde cada alocação faz diferença.
  • Se o método for mais complexo ou não for chamado repetidamente, o ganho pode ser mínimo ou inexistente em comparação ao Task.

Minimizar Boxing e Unboxing🔗

Boxing ocorre quando um tipo por valor (ex.: int, structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC., bool) é convertido para object ou para uma interfaceTrabalhando com interfaces e coleções aprimoradas em C# 13Trabalhando com interfaces e coleções aprimoradas em C# 13Descubra como as implementações padrão em interfaces e coleções aprimoradas do C# 13 otimizam a criação de códigos modulares, seguros e performáticos. implementada por ele. Unboxing é a operação inversa. Em aplicações com intensas operações de conversão, isso resulta em muitas alocações desnecessárias de objetos.

Uma dica para C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET. (e em versões anteriores também) é evitar passes redundantes por object ou interfacesTrabalhando com interfaces e coleções aprimoradas em C# 13Trabalhando com interfaces e coleções aprimoradas em C# 13Descubra como as implementações padrão em interfaces e coleções aprimoradas do C# 13 otimizam a criação de códigos modulares, seguros e performáticos. genéricas quando não for essencial. Em vez disso, use tipos genéricos fortes e overloads de método dedicadas a tipos valor, sempre que possível.

Structs Imutáveis e readonly struct🔗

Quando você decide usar structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC., considere se ela pode ser imutável. Structs imutáveis previnem modificações acidentais no estado interno, o que favorece otimizações do compilador. Já a palavra-chave readonly garante que todos os campos da structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. são apenas leitura, fornecendo mais segurança para otimizações ao gerar código compilado.

public readonly struct Ponto2D
{
    public Ponto2D(double x, double y)
    {
        X = x;
        Y = y;
    }
    public double X { get; }
    public double Y { get; }
}

Benefícios:

  • Menos gastos de cópia e defesa de estado.
  • Maior clareza de que o valor não será alterado após a criação.

Exemplos de Comparação de Desempenho🔗

Para ilustrar, abaixo está uma pequena tabela de situações de uso onde cada escolha traz impacto no desempenho (valores hipotéticos apenas para exemplificar a ideia):

SituaçãoAbordagem RecomendadaMotivo
Alto volume de acesso a arraysSpan<T>Evita cópias, reduz alocações desnecessárias
Estrutura leve e imutávelreadonly struct ou record structElimina overhead de heap, facilita otimizadores
Operações assíncronas simplesValueTaskEvita alocação de Task no heap em cenários de rápido retorno
Herança desnecessáriasealed classPermite otimizações de inlining e devirtualização no CLR
Conversões frequentesEvitar boxing/unboxing por objectMinimiza alocações e tempo de casting

Conclusão🔗

Nesta etapa do aprendizado, vimos como o uso correto de tipos é essencial para aplicar otimizações de desempenho em C# 13O que é C# 13 e por que aprender em 90 minutosO que é C# 13 e por que aprender em 90 minutosExplore as inovações do C# 13 e melhore a legibilidade do seu código. Este tutorial prático de 90 minutos oferece dicas essenciais para desenvolvedores .NET., mantendo a legibilidade. Ao prestar atenção às distinções entre structBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. e classBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC., empregar Span<T> para manipular coleções sem overhead, escolher ValueTask em cenários assíncronos pontuais e selar classesBoas práticas de performance e memória para jogos em C# 13Boas práticas de performance e memória para jogos em C# 13Descubra técnicas para otimizar desempenho e uso de memória em jogos com C# 13, utilizando structs, pooling e melhores práticas do GC. que não requerem herança, você atinge um código mais responsivo, com melhor gerenciamento de memória.

A chave está em entender o seu domínio e escolher o tipo mais adequado às necessidades de cada segmento de código. Pequenos ajustes podem gerar grandes ganhos acumulados em aplicações de grande porte ou em cenários de alta concorrência.

Próximos Passos

Com essas práticas, você se aproxima de um código robusto, com alta eficiência e pronto para lidar com cenários escaláveis.

Autor: Marcelo V. Souza - Engenheiro de Sistemas e Entusiasta em IoT e Desenvolvimento de Software, com foco em inovação tecnológica.

Referências🔗

  • Documentação Oficial Microsoft sobre C# - Fornece as bases oficiais e detalhadas sobre a linguagem e os recursos apresentados no tutorial, como tipos de valor, Span<T>, e otimizações no gerenciamento de memória: docs.microsoft.com/pt-br/dotnet/csharp/

Compartilhar artigo

Artigos Relacionados