C#: Visão geral dos recursos avançados

1. Reflexão

A reflexão permite que um programa inspecione e manipule suas próprias informações de tipo em tempo de execução. A classe Type é o núcleo da reflexão, fornecendo acesso a metadados como campos, propriedades e métodos.

(1) Obtendo informações sobre tipos

CSHARP
using System;
using System.Reflection;

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void SayHello() => Console.WriteLine("Hello!");
}

class Program
{
    static void Main()
    {
        Type type1 = typeof(Person);
        Type type2 = new Person().GetType();

        Console.WriteLine($"Type name: {type1.Name}");

        Console.WriteLine("Properties:");
        foreach (PropertyInfo p in type1.GetProperties())
            Console.WriteLine($"  {p.Name} ({p.PropertyType.Name})");

        Console.WriteLine("Methods:");
        foreach (MethodInfo m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            Console.WriteLine($"  {m.Name}");
    }
}
TEXT 📖 Somente leitura
Type name: Person
Properties:
  Name (String)
  Age (Int32)
Methods:
  get_Name
  set_Name
  get_Age
  set_Age
  SayHello

(2) Criação dinâmica de instâncias

CSHARP
using System;
using System.Reflection;

class Dog
{
    public string Breed { get; set; } = "Unknown";
    public override string ToString() => $"Dog: {Breed}";
}

class Program
{
    static void Main()
    {
        Type type = typeof(Dog);
        object obj = Activator.CreateInstance(type)!;

        PropertyInfo prop = type.GetProperty("Breed")!;
        prop.SetValue(obj, "Husky");

        Console.WriteLine(obj);
    }
}
TEXT 📖 Somente leitura
Dog: Husky

▶ Exemplo

CSHARP
using System;
using System.Linq;
using System.Reflection;

class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Multiply(double x, double y) => x * y;
    public void Reset() { }
}

class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();
        Type type = calc.GetType();

        Console.WriteLine($"Type: {type.Name}");
        Console.WriteLine($"Namespace: {type.Namespace}");
        Console.WriteLine($"Is class: {type.IsClass}");

        Console.WriteLine("Public methods:");
        MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        foreach (MethodInfo m in methods)
        {
            string parameters = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
            Console.WriteLine($"  {m.ReturnType.Name} {m.Name}({parameters})");
        }
    }
}
▶ Experimente
TEXT 📖 Somente leitura
Type: Calculator
Namespace: 
Is class: True
Public methods:
  Int32 Add(Int32 a, Int32 b)
  Double Multiply(Double x, Double y)
  Void Reset()

2. Atributos

Atributos são marcadores declarativos usados para adicionar metadados a elementos de código. É possível usar atributos embutidos ou definir atributos personalizados.

(1) Atributos embutidos

CSHARP
using System;

class LegacyService
{
    [Obsolete("Please use NewService instead", error: false)]
    public void OldMethod() => Console.WriteLine("Old method");

    public void NewMethod() => Console.WriteLine("New method");
}

class Program
{
    static void Main()
    {
        var service = new LegacyService();
        service.OldMethod();
        service.NewMethod();
    }
}
TEXT 📖 Somente leitura
Old method
New method
💡 Quando o parâmetro error de Obsolete for definido como true, chamar o método antigo resultará em um erro de compilação, em vez de um aviso.

(2) Atributos personalizados

CSHARP
using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class DescriptionAttribute : Attribute
{
    public string Text { get; }
    public DescriptionAttribute(string text) => Text = text;
}

[Description("User management service")]
class UserService
{
    [Description("Get all users")]
    public void GetAll() { }
}

class Program
{
    static void Main()
    {
        var attr = Attribute.GetCustomAttribute(typeof(UserService), typeof(DescriptionAttribute)) as DescriptionAttribute;
        Console.WriteLine(attr?.Text ?? "No description");
    }
}
TEXT 📖 Somente leitura
User management service

3. Restrições genéricas em detalhes

As restrições genéricas utilizam a palavra-chave where para restringir o intervalo dos parâmetros de tipo, garantindo que eles atendam a condições específicas.

(1) Visão geral dos tipos de restrição

Restrição Descrição
where T : class T deve ser um tipo de referência
where T : struct T deve ser um tipo de valor (não pode ser nulo)
where T : new() T deve ter um construtor público sem parâmetros
where T : ISpecific T deve implementar a interface especificada
where T : BaseClass T deve herdar da classe base especificada
where T : unmanaged T deve ser um tipo não gerenciado
where T : notnull T deve ser um tipo que não pode ser nulo

▶ Exemplo

CSHARP
using System;

interface IRepository
{
    string GetName();
}

class SqlRepository : IRepository
{
    public string GetName() => "SQL Repository";
}

class Service```<T>``` where T : class, IRepository, new()
{
    private T _repo = new T();
    public void Print() => Console.WriteLine(_repo.GetName());
}

class Program
{
    static void Main()
    {
        var service = new Service<SqlRepository>();
        service.Print();
    }
}
▶ Experimente
TEXT 📖 Somente leitura
SQL Repository
⚠️ A restrição new() deve aparecer por último na lista de restrições; class ou struct devem vir primeiro.


4. Iteradores (yield)

Os iteradores utilizam yield return para gerar os elementos da sequência um por um, implementando a avaliação preguiçosa — os elementos são calculados somente quando iterados.

(1) rendimento e quebra de rendimento

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable```<int>``` GetNumbers()
    {
        for (int i = 0; i < 5; i++)
        {
            if (i == 3) yield break;
            yield return i;
        }
    }

    static void Main()
    {
        foreach (int n in GetNumbers())
            Console.WriteLine(n);
    }
}
TEXT 📖 Somente leitura
0
1
2
💡 yield break encerra imediatamente a iteração. Os métodos do iterador retornam IEnumerable<T> ou IEnumerator<T>.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable```<int>``` Fibonacci(int count)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < count; i++)
        {
            yield return a;
            int temp = a + b;
            a = b;
            b = temp;
        }
    }

    static void Main()
    {
        Console.WriteLine("Fibonacci sequence, first 10 terms:");
        foreach (int n in Fibonacci(10))
        {
            Console.Write(n + " ");
        }
        Console.WriteLine();
    }
}
▶ Experimente
TEXT 📖 Somente leitura
Fibonacci sequence, first 10 terms:
0 1 1 2 3 5 8 13 21 34 

5. Span<T> e Memory<T>

Span<T> e Memory<T> oferecem acesso segmentado, sem cópia, a regiões contíguas de memória, o que os torna tipos essenciais para cenários de alto desempenho.

(1) Span<T> Uso básico

CSHARP
using System;

class Program
{
    static void Main()
    {
        int[] array = { 10, 20, 30, 40, 50 };
        Span```<int>``` span = array.AsSpan();
        Span```<int>``` slice = span.Slice(1, 3);

        slice[0] = 99;

        Console.WriteLine(string.Join(", ", array));

        Span```<int>``` stackSpan = stackalloc int[3];
        stackSpan[0] = 1;
        stackSpan[1] = 2;
        stackSpan[2] = 3;
        Console.WriteLine(string.Join(", ", stackSpan.ToArray()));
    }
}
TEXT 📖 Somente leitura
10, 99, 30, 40, 50
1, 2, 3
⚠️ Span<T> só pode ser armazenado na pilha; não pode ser usado como campo de classe nem além de um limite de await. Use Memory<T> quando precisar abranger vários quadros da pilha.


6. Expressões regulares (Regex)

A classe System.Text.RegularExpressions.Regex é usada para correspondência de padrões, pesquisa e substituição de texto.

(1) Operações comuns

CSHARP
using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        string input = "Contact phone: 138-1234-5678, Email: test@example.com";

        bool hasPhone = Regex.IsMatch(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Contains phone: {hasPhone}");

        Match phoneMatch = Regex.Match(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Phone: {phoneMatch.Value}");

        MatchCollection emails = Regex.Matches(input, @"[\w.]+@[\w.]+");
        foreach (Match m in emails)
            Console.WriteLine($"Email: {m.Value}");

        string replaced = Regex.Replace(input, @"\d{4}", "****");
        Console.WriteLine($"Replaced: {replaced}");
    }
}
TEXT 📖 Somente leitura
Contains phone: True
Phone: 138-1234-5678
Email: test@example.com
Replaced: Contact phone: 138-****-****, Email: test@example.com

(2) Referência rápida aos padrões comuns

Padrão Descrição
\d Dígito
\w Letra/dígito/traço inferior
\s Espaço em branco
. Qualquer caractere (exceto quebra de linha)
^ / $ Início da linha / Fim da linha
{n,m} Repita n a m vezes
[abc] Conjunto de caracteres

7. Código inseguro

O contexto unsafe permite o uso de ponteiros e operações manuais de memória, aplicável em cenários de interoperabilidade ou de desempenho extremo.

(1) Ponteiros e valores fixos

CSHARP
using System;

class Program
{
    static unsafe void Main()
    {
        int value = 42;
        int* ptr = &value;

        Console.WriteLine($"Value: {*ptr}");
        Console.WriteLine($"Address: {(long)ptr}");

        int[] arr = { 1, 2, 3 };
        fixed (int* p = arr)
        {
            Console.WriteLine($"First element: {*p}");
            Console.WriteLine($"Second element: {*(p + 1)}");
        }
    }
}
TEXT 📖 Somente leitura
Value: 42
Address: 12345678
First element: 1
Second element: 2
⚠️ O uso de unsafe requer que <AllowUnsafeBlocks> seja ativado; a instrução true`` in the project file. The fixed fixa um objeto para impedir que o GC mova a memória.


8. Visão geral dos recursos das versões do C# (7–12)

Versão Principais recursos Breve descrição
C# 7 variáveis out, tuplas, correspondência de padrões out int x declaração inline, (int, string) tuplas, is Type padrão
C# 7 funções locais, retorno por referência funções aninhadas em métodos, valores de retorno por referência
C# 8 tipos de referência nulos, fluxos assíncronos string? anotação de nulidade, IAsyncEnumerable<T>
C# 8 métodos padrão de interface, índices e intervalos Implementações padrão de interface, [^1], [1..3]
C# 9 tipos de registro, setter de inicialização record tipos imutáveis, init propriedades somente de inicialização
C# 9 instruções de nível superior, new com tipo de destino Sem código padrão para Main, new() omissão de tipo
C# 10 uso global de “using”, namespaces com escopo de arquivo global using X;, namespace Ns; em uma única linha
C# 10 estruturas de registro, strings interpoladas com constantes record struct, strings interpoladas com constantes
C# 11 literais de string bruta, obrigatórios """...""" strings brutas com várias linhas, required inicialização forçada
C# 11 padrões de lista, literais UTF-8 correspondência [1, 2, ..], "hello"u8
C# 12 construtores primários class C(int x) declaração de classe como construtor
C# 12 expressões de coleção, alias para qualquer tipo [1, 2, 3] inicialização uniforme, using P = int*
💡 Esta aula é uma visão geral dos recursos avançados; cada tópico será abordado em profundidade em uma aula específica a seguir.

❓ Perguntas Frequentes

P: A reflexão tem um custo de desempenho? R: Sim, a reflexão é dezenas de vezes mais lenta do que chamadas diretas. Para uso frequente, armazene o MethodInfo em cache ou mude para árvores de expressão. P: Qual é a diferença entre Span<T> e Memory<T>? R: O Span só pode ser mantido na pilha e não pode ultrapassar os limites de assíncrono/armazenamento; o Memory pode ser mantido no heap e ultrapassar os limites de assíncrono, sendo acessado por meio de .Span. P: O retorno do yield é multithread? R: Não, o yield é um mecanismo de enumeração preguiçosa de thread único — os elementos são calculados somente quando o MoveNext() é chamado. P: Quando é necessário usar código não seguro? R: Use-o quando precisar de interoperabilidade com C/C++, operações de memória não gerenciada ou desempenho extremo; evite-o no desenvolvimento cotidiano.

📖 Resumo

📝 Exercícios

  1. Use a reflexão para obter todos os nomes dos métodos públicos da classe string e exibi-los
  2. Criar um atributo personalizado [Author], aplicá-lo a uma classe e lê-lo por meio de reflexão
  3. Escreva um método genérico T Create``<T>``() where T : new() que crie e retorne uma instância
  4. Use o yield return para implementar um iterador que gere os primeiros 20 termos da sequência de Fibonacci
  5. Use expressões regulares (Regex) para verificar se uma string está no formato válido de um endereço IPv4
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%