Comment réussir un entretien C# : les meilleures questions de niveau avancé
Un entretien pour un poste de développeur Senior C# ne teste pas seulement la syntaxe, mais aussi une compréhension approfondie du fonctionnement interne de .NET, de l’optimisation des performances et des décisions architecturales. Dans cet article, nous analysons les sujets clés qui reviennent souvent lors des entretiens techniques. Vous apprendrez comment répondre aux questions complexes et obtiendrez des exemples de code prêts à l’emploi pour démontrer vos compétences.
1. Programmation asynchrone : async/await et Task
Les questions sur async et await sont un classique de tout entretien avancé. Les recruteurs veulent voir que vous comprenez le fonctionnement du contexte de synchronisation et que vous évitez les deadlocks.
Question typique : « Que se passe-t-il si on appelle .Result ou .Wait() sur une méthode asynchrone dans un thread UI ? »
Réponse : Un deadlock se produit, car le thread principal se bloque en attendant le résultat, tandis que la continuation de la méthode asynchrone tente de revenir dans le même contexte de synchronisation, qui est occupé. La solution est d’utiliser ConfigureAwait(false) ou de rendre l’appel entièrement asynchrone.
public async Task<string> GetDataAsync(){ // Bonne gestion du contexte var result = await SomeHttpCallAsync().ConfigureAwait(false); return result;}
// Erreur : deadlock dans UI/ASP.NET Core (avant .NET Core 2.1)// var data = GetDataAsync().Result;Question complémentaire : « Quelle est la différence entre Task.Run et Task.Factory.StartNew ? »
Task.Run utilise le pool de threads par défaut et déroule automatiquement les tâches imbriquées. StartNew offre plus de contrôle (par exemple, on peut spécifier TaskCreationOptions.LongRunning), mais nécessite une gestion manuelle de Unwrap() pour les délégués asynchrones.
2. LINQ et exécution différée (Deferred Execution)
LINQ est un outil puissant, mais une mauvaise utilisation peut entraîner une baisse des performances. Les questions avancées portent souvent sur IQueryable vs IEnumerable et la matérialisation des requêtes.
Question : « Pourquoi le code suivant effectue-t-il plusieurs requêtes à la base de données ? »
var query = context.Products.Where(p => p.Price > 100);foreach (var product in query){ Console.WriteLine(product.Name);}var count = query.Count(); // Deuxième requête !Réponse : IQueryable représente une expression (Expression Tree) qui est traduite en SQL. Chaque fois que vous itérez ou appelez une fonction d’agrégation (Count, Sum), le SQL est envoyé à nouveau. Pour éviter cela, il faut matérialiser les données : var list = query.ToList();
Nuance avancée : La différence entre First() et FirstOrDefault() — First() lève une exception si la séquence est vide, ce qui peut être plus coûteux en termes de gestion des erreurs.
3. Gestion de la mémoire et Garbage Collector (GC)
Comprendre le fonctionnement du GC est un signe de développeur Senior. Les questions peuvent porter sur les générations (Generations), la finalisation et le pattern Dispose.
Question : « Qu’est-ce que le Large Object Heap (LOH) et pourquoi faut-il être prudent avec lui ? »
Réponse : Le LOH est le tas pour les objets de taille supérieure à 85 000 octets. Contrairement au Small Object Heap, le LOH n’est pas compacté (avant .NET 4.5.1), ce qui entraîne une fragmentation de la mémoire. Cela peut provoquer une OutOfMemoryException, même s’il y a beaucoup de mémoire libre. Recommandations : utiliser des pools d’objets ou diviser les grands tableaux en parties.
// Exemple de mauvaise pratiquebyte[] largeBuffer = new byte[100_000]; // Atterrit dans le LOH
// Mieux : utiliser ArrayPoolbyte[]