Entrevista avançada de C#: como surpreender o empregador e não fracassar
\n\nA entrevista para uma posição de Senior ou Middle .NET developer não é apenas uma verificação de sintaxe. Os entrevistadores querem ver uma compreensão profunda do funcionamento interno da plataforma, a capacidade de escrever código performático e tomar decisões arquitetônicas. Neste artigo, analisaremos os principais tópicos e perguntas de nível avançado que o ajudarão a passar em uma entrevista técnica de C#.
\n\n1. Programação assíncrona: async/await e Task
\n\nEste é, talvez, o tópico mais popular em entrevistas. Não basta dizer que você conhece async/await. É preciso entender como ele funciona internamente.
Pergunta: O que é SynchronizationContext e como ele afeta a execução do await?
\nPergunta clássica. SynchronizationContext é uma abstração que permite agendar a execução de código em um contexto específico (por exemplo, na thread de UI do WPF ou no pool de threads do ASP.NET). Quando você usa await, após a conclusão da operação assíncrona, o código por padrão tenta retornar ao contexto original. Em aplicações de console, é o pool de threads; em UI, é a thread do dispatcher. Compreender esse mecanismo ajuda a evitar deadlocks.
Pergunta: Qual é a diferença entre Task.Run e Task.Factory.StartNew?
\nÀ primeira vista, eles são semelhantes, mas Task.Run é um wrapper simplificado. Task.Factory.StartNew oferece mais controle (por exemplo, você pode definir TaskCreationOptions ou TaskScheduler). No entanto, por padrão, Task.Run usa TaskScheduler.Default (pool de threads), enquanto Task.Factory.StartNew pode usar o agendador atual, o que às vezes leva a comportamentos inesperados.
// Exemplo de deadlock devido ao SynchronizationContext (em aplicação UI)\npublic async Task<string> GetDataAsync()\n{\n var result = await SomeAsyncMethod(); // captura do contexto\n return result.ToString(); // tenta retornar à thread da UI\n}\n\n// Se chamar .Result ou .Wait() na task, ocorrerá deadlock\nvar data = GetDataAsync().Result; // RUIM!\n\n\n2. Gerenciamento de memória e Garbage Collector (GC)
\n\nUm desenvolvedor avançado deve entender como o .NET gerencia a memória para evitar vazamentos e otimizar a performance.
\n\nPergunta: Explique as gerações do GC (Gen 0, Gen 1, Gen 2) e o Large Object Heap (LOH).
\nO GC no .NET usa um modelo geracional. Os objetos primeiro vão para a Gen 0 (a coleta ocorre com frequência). Se sobrevivem à coleta, passam para a Gen 1 e depois para a Gen 2. O LOH é destinado a objetos com mais de 85.000 bytes. Ele não é compactado por padrão (até o .NET 4.5.1), o que pode causar fragmentação. A pergunta pode ser aprofundada: "Como funciona a coleta de lixo em segundo plano?" ou "O que são os modos GC: Workstation vs Server?"
\n\nPergunta: O que é finalização (Finalization) e como evitar problemas relacionados a ela?
\nOs finalizadores (~Finalizer()) retardam a coleta de lixo, pois objetos com finalizadores entram na fila de finalização e exigem duas passagens do GC. O padrão correto é implementar IDisposable com o padrão Dispose.
public class ResourceHolder : IDisposable\n{\n private IntPtr _handle;\n private bool _disposed = false;\n\n public void Dispose()\n {\n Dispose(true);\n GC.SuppressFinalize(this);\n }\n\n protected virtual void Dispose(bool disposing)\n {\n if (!_disposed)\n {\n if (disposing)\n {\n // Libera recursos gerenciados\n }\n // Libera recursos não gerenciados (handle)\n _disposed = true;\n }\n }\n\n ~ResourceHolder()\n {\n Dispose(false);\n }\n}\n\n\n3. Delegados, eventos e LINQ: da sintaxe à compreensão
\n\nEsses tópicos frequentemente