Primary Constructors (Classes)
Enfin disponible pour les classes (pas seulement les records) ! Plus besoin de boilerplate pour l'injection de dépendances :
Avant C# 12
public class ProductService
{
private readonly IProductRepository _repository;
private readonly ILogger<ProductService> _logger;
public ProductService(IProductRepository repository, ILogger<ProductService> logger)
{
_repository = repository;
_logger = logger;
}
public async Task<Product> GetById(int id)
{
_logger.LogInformation("Getting product {Id}", id);
return await _repository.GetByIdAsync(id);
}
}
Avec C# 12
public class ProductService(IProductRepository repository, ILogger<ProductService> logger)
{
public async Task<Product> GetById(int id)
{
logger.LogInformation("Getting product {Id}", id);
return await repository.GetByIdAsync(id);
}
}
Moins de code, même fonctionnalité. Les paramètres sont accessibles dans toute la classe.
Collection Expressions
Une syntaxe unifiée pour créer des collections :
// Avant
int[] numbers = new int[] { 1, 2, 3 };
List<string> names = new List<string> { "Alice", "Bob" };
Span<int> span = stackalloc int[] { 1, 2, 3 };
// Avec C# 12
int[] numbers = [1, 2, 3];
List<string> names = ["Alice", "Bob"];
Span<int> span = [1, 2, 3];
Spread operator
int[] first = [1, 2, 3];
int[] second = [4, 5, 6];
int[] combined = [..first, ..second]; // [1, 2, 3, 4, 5, 6]
// Pratique pour les options par défaut
string[] defaultTags = ["important"];
string[] userTags = ["urgent", "review"];
string[] allTags = [..defaultTags, ..userTags];
Alias de types (using)
Vous pouvez maintenant créer des alias pour n'importe quel type :
using Point = (int X, int Y);
using UserId = System.Guid;
using ProductList = System.Collections.Generic.List<Product>;
public class Example
{
public Point Location { get; set; } = (0, 0);
public UserId Id { get; set; } = UserId.NewGuid();
public ProductList Products { get; set; } = [];
}
Améliore la lisibilité, surtout pour les tuples et types génériques complexes.
Default Lambda Parameters
// Paramètres par défaut dans les lambdas
var greet = (string name = "World") => $"Hello, {name}!";
Console.WriteLine(greet()); // "Hello, World!"
Console.WriteLine(greet("Alice")); // "Hello, Alice!"
Inline Arrays
Pour les scénarios haute performance :
[InlineArray(10)]
public struct Buffer
{
private int _element;
}
// Utilisation
var buffer = new Buffer();
buffer[0] = 42;
buffer[1] = 43;
Évite les allocations heap pour les petits tableaux de taille fixe.
.NET 8 : Améliorations performance
- Native AOT amélioré : Temps de démarrage réduit, empreinte mémoire minimale
- Frozen Collections : Collections immuables optimisées pour la lecture
- Time abstraction : Testabilité améliorée avec TimeProvider
- UTF8 literals : Support natif des chaînes UTF-8
Frozen Collections
using System.Collections.Frozen;
// Création (une seule fois, coûteux)
FrozenDictionary<string, int> lookup = new Dictionary<string, int>
{
["one"] = 1,
["two"] = 2,
["three"] = 3
}.ToFrozenDictionary();
// Lecture (très rapide)
int value = lookup["two"];
Conclusion
C# 12 apporte des améliorations de qualité de vie significatives. Les primary constructors et collection expressions sont mes préférées - elles réduisent le boilerplate sans sacrifier la lisibilité.
Besoin d'aide pour moderniser votre codebase C# ? Contactez-moi.