Qual a diferença entre tipos de valor (value types) e tipos de referência (reference types) em C#?
Tipos de valor (int, double, struct, enum, bool) são armazenados na stack e contêm o dado diretamente. Tipos de referência (class, string, array, delegate) são armazenados na heap e a variável contém apenas um ponteiro/referência para o objeto. Ao copiar um value type, cria-se uma cópia independente; ao copiar um reference type, ambas as variáveis apontam para o mesmo objeto.
O que é boxing e unboxing em C#?
Boxing é o processo de converter um value type em object (reference type), alocando memória na heap. Unboxing é o inverso: extrair o value type de dentro do object. Boxing/unboxing tem custo de performance e deve ser evitado em loops ou código de alto desempenho. Exemplo: int x = 42; object o = x; (boxing) int y = (int)o; (unboxing).
Para que serve o modificador ‘ref’ em parâmetros de método?
O modificador ‘ref’ permite passar um argumento por referência, ou seja, o método recebe uma referência direta à variável original, podendo modificá-la. A variável deve ser inicializada antes de ser passada. Diferente de ‘out’, que não exige inicialização prévia mas obriga o método a atribuir um valor.
Qual a diferença entre ‘ref’, ‘out’ e ‘in’ em parâmetros?
‘ref’: passa por referência, variável deve ser inicializada antes, pode ser lida e modificada. ‘out’: passa por referência, não precisa ser inicializada, método DEVE atribuir valor antes de retornar. ‘in’: passa por referência somente leitura, o método não pode modificar o valor. ‘in’ é útil para structs grandes para evitar cópia sem permitir modificação.
O que são records em C#? Quando usá-los?
Records são tipos de referência (record class) ou valor (record struct) imutáveis por padrão, introduzidos no C# 9. Possuem igualdade por valor (não por referência), sintaxe concisa com positional parameters, método ToString() automático e suporte a ‘with’ expressions para criar cópias modificadas. Ideais para DTOs, eventos de domínio e objetos de valor (Value Objects).
Explique o conceito de string interpolation em C#.
String interpolation permite embutir expressões diretamente em strings usando o prefixo $ e chaves {}. Exemplo: $”Olá, {nome}! Você tem {idade} anos.”. É mais legível que string.Format() ou concatenação. Pode-se aplicar formatação: $”{valor:C2}” para moeda, $”{data:dd/MM/yyyy}” para datas.
Qual a diferença entre ‘switch statement’ tradicional e ‘switch expression’ em C#?
O switch statement (clássico) usa case/break e executa blocos de código. O switch expression (C# 8+) é uma expressão que retorna um valor, usa ‘=>’ e é mais conciso. Switch expressions suportam pattern matching avançado: type patterns, property patterns, tuple patterns e relational patterns.
O que são arrays multidimensionais vs jagged arrays em C#?
Arrays multidimensionais (int[,]) têm dimensões fixas e formam uma matriz retangular. Jagged arrays (int[][]) são arrays de arrays, onde cada sub-array pode ter tamanho diferente. Jagged arrays geralmente têm melhor performance porque o CLR otimiza arrays unidimensionais. Use multidimensionais para matrizes regulares e jagged para estruturas irregulares.
Explique a diferença entre List<T> e Array em C#.</T>
Arrays têm tamanho fixo definido na criação, acesso O(1) por índice. List<T> é uma coleção dinâmica que internamente usa um array que é redimensionado automaticamente (duplicando a capacidade). List<T> oferece métodos como Add, Remove, Contains, Sort. Use arrays quando o tamanho é conhecido e fixo; List<T> quando precisa de tamanho dinâmico.</T></T></T>
O que é um construtor em C#? Quais tipos existem?
Construtor é um método especial chamado ao criar uma instância. Tipos: (1) Construtor padrão (sem parâmetros), (2) Construtor parametrizado, (3) Construtor estático (static, executado uma vez antes do primeiro uso da classe), (4) Construtor privado (impede instanciação externa, usado em Singleton). Constructor chaining permite um construtor chamar outro com ‘this()’.
Qual a diferença entre propriedades (properties) e campos (fields)?
Campos são variáveis declaradas diretamente na classe. Propriedades encapsulam campos com get/set accessors, permitindo validação, lógica computada e controle de acesso. Propriedades podem ser auto-implementadas (get; set;), somente leitura (get;), com init-only setter (init;), ou computadas (sem backing field). Prefira propriedades para membros públicos.
O que são propriedades computadas (computed properties)?
Propriedades computadas não possuem backing field; seu valor é calculado dinamicamente a partir de outros membros. Exemplo: public decimal Total => Preco * Quantidade; São somente leitura e recalculadas a cada acesso. Úteis para valores derivados que devem estar sempre sincronizados com os dados originais.
Qual a diferença entre classes estáticas e métodos estáticos?
Classes estáticas não podem ser instanciadas e só podem conter membros estáticos. São seladas (sealed) implicitamente. Métodos estáticos pertencem à classe, não a uma instância, e não acessam membros de instância. Use classes estáticas para utilitários e helpers. Métodos estáticos são úteis para operações que não dependem do estado do objeto.
Explique herança em C#. Qual a diferença entre ‘virtual’ e ‘abstract’?
Herança permite que uma classe derivada herde membros de uma classe base. ‘virtual’ marca um método que TEM implementação padrão mas PODE ser sobrescrito com ‘override’. ‘abstract’ marca um método que NÃO TEM implementação e DEVE ser sobrescrito nas classes derivadas. Classes com membros abstract devem ser declaradas como abstract e não podem ser instanciadas.
O que é polimorfismo? Dê exemplos.
Polimorfismo permite tratar objetos de diferentes tipos de forma uniforme. Em C#: (1) Polimorfismo de subtipo: uma variável do tipo base pode referenciar objetos derivados (Animal a = new Dog()). (2) Polimorfismo paramétrico: Generics (List<T>). (3) Polimorfismo ad-hoc: sobrecarga de métodos (overloading). O método chamado é resolvido em runtime (virtual dispatch) ou compile-time (overloading).</T>
Qual a diferença entre interfaces e classes abstratas em C#?
Interfaces: contrato puro (antes do C# 8), suportam herança múltipla, não têm estado (campos), desde C# 8 podem ter implementação padrão. Classes abstratas: podem ter estado (campos), construtores, membros com implementação, mas só suportam herança simples. Use interfaces para definir capacidades (IDisposable, IEnumerable); classes abstratas para compartilhar código entre classes relacionadas.
O que são Extension Methods? Quando usá-los?
Extension Methods permitem adicionar métodos a tipos existentes sem modificá-los ou criar subclasses. São métodos estáticos em classes estáticas, com o primeiro parâmetro precedido por ‘this’. Exemplo: public static bool IsNullOrEmpty(this string s). Usados extensivamente em LINQ. Úteis para adicionar funcionalidade a tipos que você não controla.
Explique o operador ‘is’ e pattern matching em C#.
O operador ‘is’ verifica se um objeto é de um determinado tipo. Com pattern matching (C# 7+): ‘is Type variavel’ faz verificação e cast simultaneamente. C# 8+ adicionou: property patterns (obj is { Prop: value }), tuple patterns, positional patterns. C# 9+: relational patterns (is > 0 and < 100), logical patterns (is not null).
O que são delegates em C#?
Delegates são tipos que representam referências a métodos com assinatura específica. São type-safe function pointers. Delegates built-in: Action (sem retorno), Func<T,TResult> (com retorno), Predicate<T> (retorna bool). Delegates são a base para eventos e são usados extensivamente com LINQ e programação funcional em C#.</T>
Como funcionam eventos em C#?
Eventos são baseados em delegates e implementam o padrão Observer. Declarados com ‘event’ keyword, restringem operações externas a += (subscribe) e -= (unsubscribe), impedindo invocação direta ou reatribuição fora da classe. O publisher dispara o evento; subscribers registram handlers. Padrão: public event EventHandler<TEventArgs> NomeEvento.</TEventArgs>
Explique try-catch-finally em C#.
try: bloco de código que pode lançar exceções. catch: captura e trata exceções específicas (pode ter múltiplos catches, do mais específico ao mais genérico). finally: sempre executa, independente de exceção (usado para liberar recursos). ‘using’ statement é syntactic sugar para try-finally com IDisposable. Sempre capture exceções específicas, nunca apenas Exception genérico em produção.
Quando criar exceções customizadas? Como implementar?
Crie exceções customizadas quando erros de domínio precisam de contexto adicional que exceções padrão não fornecem. Herde de Exception (ou ApplicationException). Implemente pelo menos 3 construtores: sem parâmetros, com mensagem, e com mensagem + inner exception. Adicione propriedades para dados de contexto. Exemplo: InvalidTransactionException com TransactionId.
O que é rethrowing de exceções? Qual a diferença entre ‘throw’ e ‘throw ex’?
‘throw;’ (sem argumento) relança a exceção preservando o stack trace original. ‘throw ex;’ relança mas RESETA o stack trace, perdendo informação de onde o erro originou. Sempre use ‘throw;’ para rethrow. Use ‘throw new Exception(msg, ex)’ para wrapping, passando a exceção original como InnerException.
Explique os principais métodos LINQ: Where, Select, OrderBy, GroupBy.
Where: filtra elementos por condição (como SQL WHERE). Select: projeta/transforma cada elemento (como SQL SELECT). OrderBy/OrderByDescending: ordena elementos. GroupBy: agrupa elementos por chave, retornando IGrouping<TKey, TElement>. Todos usam deferred execution (executam apenas quando o resultado é iterado). São extension methods em IEnumerable<T>.</T>