Como passar em uma entrevista de C#: principais perguntas de nível avançado
A entrevista para a posição de Desenvolvedor Senior C# não é apenas uma verificação de sintaxe, mas também um profundo entendimento do funcionamento interno do .NET, otimização de desempenho e decisões arquiteturais. Neste artigo, analisaremos os tópicos-chave que frequentemente surgem em entrevistas técnicas. Você aprenderá como responder a perguntas difíceis e obterá exemplos de código prontos para demonstrar suas habilidades.
1. Programação assíncrona: async/await e Task
Perguntas sobre async e await são clássicas em qualquer entrevista avançada. Os entrevistadores querem ver se você entende como funciona o contexto de sincronização e evita deadlocks.
Pergunta típica: "O que acontece se você chamar .Result ou .Wait() em um método assíncrono em uma thread de UI?"
Resposta: Ocorrerá um deadlock, pois a thread principal será bloqueada aguardando o resultado, enquanto a continuação do método assíncrono tentará retornar ao mesmo contexto de sincronização que está ocupado. A solução é usar ConfigureAwait(false) ou tornar a chamada completamente assíncrona.
public async Task<string> GetDataAsync(){ // Trabalho correto com o contexto var result = await SomeHttpCallAsync().ConfigureAwait(false); return result;}
// Erro: deadlock em UI/ASP.NET Core (antes do .NET Core 2.1)// var data = GetDataAsync().Result;Pergunta adicional: "Qual a diferença entre Task.Run e Task.Factory.StartNew?"
Task.Run usa o pool de threads padrão e desencapsula automaticamente tarefas aninhadas. StartNew oferece mais controle (por exemplo, você pode especificar TaskCreationOptions.LongRunning), mas requer gerenciamento manual de Unwrap() para delegados assíncronos.
2. LINQ e Execução Adiada (Deferred Execution)
LINQ é uma ferramenta poderosa, mas seu uso incorreto pode levar a quedas de desempenho. Perguntas avançadas frequentemente abordam IQueryable vs IEnumerable e a materialização de consultas.
Pergunta: "Por que o código a seguir faz várias consultas ao banco de dados?"
var query = context.Products.Where(p => p.Price > 100);foreach (var product in query){ Console.WriteLine(product.Name);}var count = query.Count(); // Segunda consulta!Resposta: IQueryable representa uma expressão (Expression Tree) que é traduzida em SQL. Cada vez que você itera ou chama uma função agregada (Count, Sum), o SQL é enviado novamente. Para evitar isso, é necessário materializar os dados: var list = query.ToList();
Nuance avançada: A diferença entre First() e FirstOrDefault() — First() lança uma exceção se a sequência estiver vazia, o que pode ser mais custoso em termos de tratamento de erros.
3. Gerenciamento de memória e Garbage Collector (GC)
Entender o funcionamento do GC é um sinal de um desenvolvedor Senior. As perguntas podem abordar gerações (Generations), finalização e o padrão Dispose.
Pergunta: "O que é o Large Object Heap (LOH) e por que devemos ter cuidado com ele?"
Resposta: LOH é o heap para objetos com tamanho superior a 85.000 bytes. Ao contrário do Small Object Heap, o LOH não sofre compactação (até .NET 4.5.1), o que leva à fragmentação da memória. Isso pode causar OutOfMemoryException, mesmo que haja muita memória livre. Recomendações: usar pools de objetos ou dividir grandes arrays em partes.
// Exemplo de má práticabyte[] largeBuffer = new byte[100_000]; // Cai no LOH
// Melhor: usar ArrayPoolbyte[]