🔹 1. Quelle est la différence entre .NET Framework, .NET Core et .NET (5 et supérieur) ?
| Plateforme | Période | Compatibilité | OS supportés | Objectif |
|---|---|---|---|---|
| .NET Framework | 2002 → 2019 | Windows uniquement | Windows | Ancien framework historique (.NET 4.x) |
| .NET Core | 2016 → 2020 | Cross-platform | Windows, Linux, macOS | Léger, open source, plus rapide |
| .NET (5, 6, 7, 8...) | 2020 → présent | Unifié | Cross-platform | Fusion de .NET Core et Framework |
✅ Exemple :
-
.NET Framework: applications WinForms, WPF, ASP.NET MVC classiques. -
.NET Core: microservices, API REST, containers Docker. -
.NET 8+: unification (Web, Desktop, MAUI, Cloud, etc.).
🧠 Résumé :
.NET 5+ est la plateforme unifiée, performante et multiplateforme, héritière de .NET Core.
.NET Framework est legacy, toujours utilisé dans les grandes entreprises.
🔹 2. Qu'est-ce que le Garbage Collector et comment fonctionne-t-il en .NET ?
Le Garbage Collector (GC) est le système de gestion automatique de la mémoire en .NET.
Il libère la mémoire des objets inaccessibles (non référencés).
✅ Fonctionnement :
-
Le CLR alloue les objets dans le heap managé.
-
Le GC s’exécute quand :
-
la mémoire est faible ;
-
le heap atteint un seuil critique ;
-
ou explicitement via
GC.Collect()(rarement recommandé).
-
-
Il marque les objets accessibles (“mark”), compacte le heap (“compact”), et libère les autres (“sweep”).
✅ Générations :
-
Gen 0 : objets très récents (collectés souvent)
-
Gen 1 : objets intermédiaires
-
Gen 2 : objets persistants (collectés rarement)
Exemple :
var person = new Person("Alice");
// Quand "person" sort de portée → GC peut libérer la mémoire
🧠 Résumé :
Le GC optimise l’utilisation mémoire sans intervention explicite du développeur, mais on peut le contrôler indirectement via
IDisposableouusing.
🔹 3. Différences entre List, Array et ArrayList
| Type | Générique | Taille | Performance | Type-sûr | Utilisation |
|---|---|---|---|---|---|
| Array | Non | Fixe | Très rapide | ✅ Oui | Données de taille connue |
| List | ✅ Oui | Dynamique | Très rapide | ✅ Oui | Collections dynamiques typées |
| ArrayList | ❌ Non | Dynamique | Moins rapide (boxing/unboxing) | ❌ Non | Ancien, avant les génériques (pré-.NET 2.0) |
Exemple :
int[] arr = new int[3] { 1, 2, 3 };
List<int> list = new List<int>() { 1, 2, 3 };
ArrayList al = new ArrayList() { 1, "two", 3.0 }; // hétérogène
🧠 Résumé :
Utilisez toujours
List<T>(ouObservableCollection<T>/ImmutableList<T>selon le besoin).
ArrayListest obsolète.
🔹 4. Comment fonctionne la gestion de mémoire dans .NET ?
✅ Types de mémoire :
-
Stack (pile) → stockage des variables locales et des types valeur.
-
Allocation/désallocation très rapide.
-
-
Heap managé → stockage des types référence (objets).
-
Géré par le Garbage Collector.
-
✅ Cycle de vie :
-
Allocation automatique (
new). -
Références utilisées pendant l’exécution.
-
Quand plus référencées → GC les supprime.
-
Ressources non managées doivent être libérées manuellement (
Dispose).
Exemple :
void Example()
{
int x = 5; // stack
var p = new Person(); // heap
} // p devient éligible pour le GC
🧠 Résumé :
Le CLR gère automatiquement la mémoire managée, mais les ressources non managées nécessitent une libération manuelle (
IDisposable).
🔹 5. Quelles sont les principales classes de System.IO pour la gestion des fichiers ?
| Classe | Rôle | Exemple |
|---|---|---|
File / FileInfo |
Manipulation de fichiers (création, suppression, lecture, etc.) | File.ReadAllText(path) |
Directory / DirectoryInfo |
Manipulation de dossiers | Directory.CreateDirectory(path) |
FileStream |
Lecture/écriture bas niveau (flux d’octets) | new FileStream(path, FileMode.Open) |
StreamReader / StreamWriter |
Lecture/écriture texte | new StreamReader(path) |
BinaryReader / BinaryWriter |
Lecture/écriture binaire | new BinaryWriter(stream) |
Path |
Gestion des chemins | Path.Combine(dir, file) |
Exemple :
using (var writer = new StreamWriter("data.txt"))
{
writer.WriteLine("Hello .NET");
}
string content = File.ReadAllText("data.txt");
🧠 Résumé :
System.IOfournit une API complète pour manipuler fichiers, flux, et répertoires à tous les niveaux (haut et bas).
🔹 6. Qu'est-ce que Reflection en C# et comment l’utiliser ?
Reflection permet d’examiner ou de manipuler le métadonnées et les types à l’exécution.
✅ Usages :
-
Chargement dynamique d’assemblies.
-
Inspection des classes, propriétés, méthodes, attributs.
-
Invocation dynamique (sans connaître le type à la compilation).
Exemple :
Type type = typeof(Person);
Console.WriteLine(type.FullName);
foreach (var prop in type.GetProperties())
Console.WriteLine(prop.Name);
// Invocation dynamique
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(obj, null);
🧠 Résumé :
Reflection = introspection dynamique des types.
⚠️ À utiliser avec précaution (plus lent, moins sûr).
🔹 7. Différence entre Struct et Class en C#
| Caractéristique | struct |
class |
|---|---|---|
| Type | Valeur | Référence |
| Allocation | Stack | Heap |
| Héritage | Pas d’héritage (sauf interfaces) | Oui |
| Nullabilité | Non-nullable (sauf Nullable<T>) |
Oui |
| Performance | Léger, pas de GC | Plus lourd, managé |
| Usage typique | Petits objets immuables (ex: Point, DateTime) |
Entités complexes |
Exemple :
struct Point { public int X, Y; }
class Person { public string Name; }
🧠 Résumé :
Utilisez
structpour des petits objets immuables,classpour des objets riches et complexes.
🔹 8. Comment fonctionne le Dispose pattern avec IDisposable ?
Le Dispose Pattern permet de libérer les ressources non managées (fichiers, connexions, flux…).
✅ Interface :
public interface IDisposable
{
void Dispose();
}
✅ Exemple :
public class FileHandler : IDisposable
{
private FileStream _stream;
public FileHandler(string path)
{
_stream = new FileStream(path, FileMode.Open);
}
public void Dispose()
{
_stream?.Dispose();
GC.SuppressFinalize(this); // empêche le GC d’appeler le finalizer
}
}
✅ Utilisation :
using (var handler = new FileHandler("data.txt"))
{
// traitement
} // Dispose() automatiquement appelé
🧠 Résumé :
Dispose= nettoyage explicite.
using= exécution automatique duDispose()même en cas d’exception.
🔹 9. Différence entre dynamic et var
| Mot-clé | Type déterminé | Vérifié à | Utilisation typique |
|---|---|---|---|
| var | À la compilation | Compilation | Typage implicite, mais fortement typé |
| dynamic | À l’exécution | Exécution | Liaison tardive (interop COM, JSON, reflection) |
Exemple :
var x = 10; // int
dynamic y = 10; // type inconnu jusqu’à l’exécution
x = "text"; // ❌ Erreur compile-time
y = "text"; // ✅ OK, runtime
🧠 Résumé :
var= compile-time typing (sécurité, performance).
dynamic= runtime typing (flexibilité, moins sûr).
🔹 10. Qu'est-ce qu’un Tuple en C# et quand l’utiliser ?
Un Tuple regroupe plusieurs valeurs sans créer une classe dédiée.
✅ Exemple :
var person = Tuple.Create("Alice", 25);
Console.WriteLine($"{person.Item1}, {person.Item2}");
Depuis C# 7, on préfère les ValueTuple :
(string Name, int Age) person = ("Bob", 30);
Console.WriteLine($"{person.Name}, {person.Age}");
✅ Utilisation typique :
-
Retourner plusieurs valeurs depuis une méthode :
(string name, int age) GetPerson() => ("Tom", 40);
var (n, a) = GetPerson();
🧠 Résumé :
Tuple/ValueTuple= pratique pour des retours multiples ou temporaires,
mais pour des modèles riches → créer une classe ou record.
📘 Synthèse rapide
| Thème | Résumé |
|---|---|
| .NET Framework/Core/5+ | .NET 5+ = plateforme unifiée et cross-platform |
| Garbage Collector | Nettoie la mémoire des objets non référencés |
| List vs Array | List<T> = collection dynamique typée |
| Mémoire | Stack (rapide) vs Heap (managé par GC) |
| System.IO | File, Directory, FileStream, StreamReader... |
| Reflection | Inspection et invocation dynamique de types |
| Struct vs Class | Valeur vs Référence |
| IDisposable | Libère les ressources non managées |
| dynamic vs var | Liaison tardive vs typage implicite |
| Tuple | Regroupe plusieurs valeurs sans classe dédiée |
⚙️ 1. Différence entre Span et Array en termes de performance et d’utilisation
✅ Array<T>
-
Structure de données classique stockée sur le tas (heap).
-
Accès rapide, mais implique souvent des allocations mémoire et du ramassage (GC).
-
Ne peut pas référencer une portion d’un tableau sans copier.
✅ Span<T>
-
Type stack-only (ref struct) introduit avec .NET Core.
-
Permet de manipuler une vue sur une portion de mémoire (tableau, segment de chaîne, buffer) sans allocation ni copie.
-
Travaille sur la pile (stack) → très performant, pas de GC.
🧩 Exemple :
int[] data = { 1, 2, 3, 4, 5 };
Span<int> slice = data.AsSpan(1, 3); // [2, 3, 4]
slice[0] = 99; // Modifie aussi le tableau original
🧠 Résumé :
| Type | Allocation | Peut être sur stack | Copie nécessaire | Usage |
|---|---|---|---|---|
Array<T> |
Heap | ❌ | Oui pour sous-tableaux | Données générales |
Span<T> |
Stack | ✅ | Non | Performance, buffers, slices mémoire |
⚙️ 2. Comment fonctionne Memory et comment l’utiliser pour éviter les allocations inutiles
✅ Memory<T> :
-
Similaire à
Span<T>, mais pouvant être stocké sur le heap (donc utilisable en async). -
Utilisé pour référencer sans copier une portion de mémoire, mais plus flexible que
Span<T>.
🧩 Exemple :
Memory<int> memory = new int[] { 1, 2, 3, 4, 5 };
Memory<int> subMemory = memory.Slice(1, 3);
int first = subMemory.Span[0]; // 2
⚙️ Utilisation typique :
-
Manipulation de buffers ou streams sans recréer des tableaux intermédiaires.
-
Dans des API asynchrones, où
Span<T>n’est pas utilisable (ref struct non stockable).
🧠 Résumé :
Memory<T>= version "asynchrone et persistable" deSpan<T>.
⚙️ 3. Différences entre JsonSerializer (System.Text.Json) et Newtonsoft.Json
| Critère | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| Performance | ⚡ Très rapide (allocations faibles) | Moins rapide |
| Allocation mémoire | Faible | Plus élevée |
| Null handling | Moins flexible | Très complet |
| Polymorphisme | Support limité (avant .NET 7) | Très bon support |
| Configuration | Moins riche | Très complète (JsonConverters, attributes, etc.) |
| Inclusion dans .NET | Oui (lib native) | Non (package externe) |
🧩 Exemple :
var json = JsonSerializer.Serialize(obj);
var obj2 = JsonSerializer.Deserialize<MyClass>(json);
🧠 Résumé :
System.Text.Json= rapide, moderne, intégré ;
Newtonsoft.Json= plus complet et mature pour les cas complexes.
⚙️ 4. Qu’est-ce qu’un record en C# et pourquoi est-il utile ?
✅ Définition :
Un record est un type référence immuable conçu pour les objets de données (DTO, modèles métier).
Il implémente automatiquement :
-
Equals()/GetHashCode()basés sur les valeurs -
ToString()lisible -
Un constructeur de déconstruction
🧩 Exemple :
public record Person(string Name, int Age);
var p1 = new Person("Alice", 30);
var p2 = p1 with { Age = 31 }; // Crée une nouvelle instance
🧠 Avantages :
-
Immutabilité native
-
Comparaison par valeur
-
Syntaxe compacte et lisible
⚙️ 5. Différence entre Task et ValueTask en termes de performance
| Type | Allocation | Recyclable | Cas d’usage |
|---|---|---|---|
| Task | Toujours alloue un objet | Réutilisable via cache du pool | Asynchrones généraux |
| ValueTask | Peut éviter une allocation | Non réutilisable | Méthodes très courtes / souvent synchrones |
🧩 Exemple :
async ValueTask<int> GetNumberAsync()
{
return 42; // Pas d’allocation de Task ici
}
🧠 Résumé :
ValueTask= optimisation micro-performante quand la tâche est souvent déjà complétée.
Sinon,Taskreste la norme.
⚙️ 6. Qu’est-ce que Lazy et comment l’utiliser correctement ?
✅ Objectif :
Initialiser un objet uniquement à la première utilisation, de manière thread-safe.
🧩 Exemple :
Lazy<HeavyObject> lazyObj = new Lazy<HeavyObject>(() => new HeavyObject());
var obj = lazyObj.Value; // Instantiation ici seulement
⚙️ Avantages :
-
Délaye la création d’objets coûteux (base de données, cache)
-
Thread-safe par défaut (
LazyThreadSafetyMode.ExecutionAndPublication)
🧠 Résumé :
Lazy<T>= initialisation à la demande + thread-safe.
⚙️ 7. Fonctionnement des Ref Structs et pourquoi elles sont utilisées
✅ Définition :
ref struct = structure forcée à rester sur la pile (stack).
Impossible :
-
De la boxer
-
De la stocker sur le heap
-
De l’utiliser dans une closure ou une tâche async
🧩 Exemple :
ref struct MySpanWrapper
{
public Span<int> Data;
public MySpanWrapper(Span<int> data) => Data = data;
}
🧠 Utilité :
-
Sécurité mémoire et performance
-
Utilisé par
Span<T>,ReadOnlySpan<T>,stackalloc
⚙️ 8. Qu’est-ce que le Garbage Collector Generation 0, 1, 2 et comment cela affecte les performances ?
✅ Le GC en .NET est générationnel :
-
Les objets sont classés selon leur durée de vie estimée.
| Génération | Description | Exemple d’objet |
|---|---|---|
| Gen 0 | Objets récents, ramassés très souvent | Variables locales, petits tableaux |
| Gen 1 | Objets survivants de Gen 0 | Buffers réutilisés |
| Gen 2 | Objets de longue durée | Singletons, caches, gros objets |
⚙️ Fonctionnement :
-
Le GC collecte d’abord Gen 0, puis Gen 1, et plus rarement Gen 2.
-
Moins un objet est collecté, plus il monte de génération.
🧠 Optimisation :
-
Réduire les allocations temporaires (éviter la pression GC)
-
Réutiliser les objets longs (ex :
ArrayPool<T>)
⚙️ 9. Comment fonctionne Span.Slice() et pourquoi est-il efficace ?
✅ Fonctionnement :
Slice() crée une vue (window) sur une partie du buffer sans copier les données.
🧩 Exemple :
Span<byte> buffer = stackalloc byte[10];
Span<byte> slice = buffer.Slice(2, 5);
➡️ slice pointe sur la même mémoire, mais avec des bornes différentes.
⚙️ Performance :
-
Aucune allocation
-
Pas de duplication
-
Idéal pour le parsing binaire, manipulation de buffers, etc.
🧠 Résumé :
Slice()= sous-vue mémoire ultra-rapide sans copie ni GC.
⚙️ 10. Différence entre Assembly.Load() et Assembly.LoadFrom()
| Méthode | Source | Contexte de chargement | Cas d’usage |
|---|---|---|---|
| Assembly.Load() | Nom fort (AssemblyName) |
Default Load Context | Charger un assembly déjà connu du runtime |
| Assembly.LoadFrom() | Chemin complet du fichier DLL | LoadFrom context (spécifique) | Charger dynamiquement un fichier externe |
🧩 Exemple :
var asm1 = Assembly.Load("MyLibrary");
var asm2 = Assembly.LoadFrom(@"C:\libs\MyLibrary.dll");
⚠️ Attention :
-
LoadFrom()peut charger plusieurs copies du même assembly s’il est dans des chemins différents → erreurs de type mismatch. -
Load()est plus sûr quand l’assembly est référencé ou dans le GAC.
🧠 Résumé :
Load()= pour assemblies connus,
LoadFrom()= pour plugins ou chargements dynamiques.
📘 SYNTHÈSE COMPLÈTE
| Concept | Description | Performance |
|---|---|---|
Span<T> |
Vue stack-only sur mémoire | ⚡ Très haute |
Memory<T> |
Vue heap-safe, async compatible | ⚡ Haute |
JsonSerializer vs Newtonsoft.Json |
Nouveau moteur JSON vs historique | 🟢 Intégré et rapide |
record |
Type immuable à comparaison par valeur | 🔒 Lisible et sûr |
ValueTask |
Moins d’allocations que Task | ⚡ Micro-optimisation |
Lazy<T> |
Initialisation à la demande | 💡 Thread-safe |
ref struct |
Structure stack-only (Span, ReadOnlySpan) | ⚡ Très rapide |
| GC Generations | Cycle de vie objets (Gen0-2) | ⚙️ Optimisation mémoire |
Slice() |
Sous-vue sur mémoire sans copie | ⚡ Ultra performant |
Assembly.LoadFrom() |
Chargement dynamique depuis fichier | ⚠️ À utiliser prudemment |
Aucun commentaire:
Enregistrer un commentaire