Extension-properties
Det var länge omöjligt att lägga till properties på typer du inte äger. Med det nya extension-blocket kan du nu göra det — och samla metoder, properties och indexerare i ett och samma block.
När du läst detta ska du kunna
- Skriva extension-properties med
extension-blocket - Blanda metoder och properties i samma block
- Skriva statiska extension-members
- Förklara varför det nya sättet är bättre än det gamla
Det gamla sättet vs det nya
Det gamla sättet (statisk klass med this) funkar fortfarande, men kan bara lägga till metoder — inte properties:
// Gammalt sätt — bara metoderpublic static class StringExtensions{ public static bool IsEmail(this string s) => s.Contains('@') && s.Contains('.');}Det nya extension-blocket kan lägga till metoder, properties och indexerare i ett sammanhållet block:
// Nytt sätt — metoder, properties och indexerare tillsammanspublic static class StringExtensions{ extension (string s) { // Property — omöjligt med gamla sättet public bool IsEmail => s.Contains('@') && s.Contains('.');
// Property med beräkning public string Reversed => new(s.Reverse().ToArray());
// Metod — fungerar precis som förut public string Repeat(int times) => string.Concat(Enumerable.Repeat(s, times)); }}Extension-properties
public static class StringExtensions{ extension (string s) { public bool IsEmpty => s.Length == 0; public bool IsNotEmpty => s.Length > 0; public int WordCount => s.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length; public string FirstWord => s.Split(' ')[0]; public string TitleCase => System.Globalization.CultureInfo.CurrentCulture .TextInfo.ToTitleCase(s.ToLower()); }}string text = "hej världen det här är kul";
Console.WriteLine(text.IsEmpty); // FalseConsole.WriteLine(text.WordCount); // 5Console.WriteLine(text.FirstWord); // hejConsole.WriteLine(text.TitleCase); // Hej Världen Det Här Är KulConsole.WriteLine("".IsEmpty); // TrueEn property som refererar en annan extension-property
Extension-properties i samma block kan referera varandra precis som vanliga properties:
public static class DateTimeExtensions{ extension (DateTime dt) { public bool IsWeekend => dt.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday; public bool IsWorkday => !dt.IsWeekend; // refererar IsWeekend direkt
public string FriendlyName => dt switch { { DayOfWeek: DayOfWeek.Monday } => "Måndag", { DayOfWeek: DayOfWeek.Friday } => "Fredag 🎉", _ => dt.DayOfWeek.ToString() }; }}var today = DateTime.Today;Console.WriteLine(today.IsWeekend); // True/FalseConsole.WriteLine(today.IsWorkday); // motsatsenConsole.WriteLine(today.FriendlyName); // t.ex. "Fredag 🎉"IsWorkday läser IsWeekend utan att behöva gå via dt.IsWeekend — blocket ser sina egna members direkt.
Se även: C# 14: Extension properties på marcusmedina.pro
Blanda metoder och properties
Det riktiga värdet av extension-blocket är att du kan samla allt som hör ihop:
public static class ListExtensions{ extension<T> (List<T> list) { // Properties public bool IsEmpty => list.Count == 0; public T First => list[0]; public T Last => list[^1]; public T Middle => list[list.Count / 2];
// Metoder public void Shuffle() { var rng = Random.Shared; for (int i = list.Count - 1; i > 0; i--) { int j = rng.Next(i + 1); (list[i], list[j]) = (list[j], list[i]); } }
public List<T> Duplicated() => [..list, ..list]; }}var numbers = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine(numbers.IsEmpty); // FalseConsole.WriteLine(numbers.First); // 1Console.WriteLine(numbers.Last); // 5Console.WriteLine(numbers.Middle); // 3
var doubled = numbers.Duplicated(); // [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]Statiska extension-members
extension-blocket kan också lägga till statiska members — något det gamla sättet inte stöder alls:
public static class DateTimeExtensions{ extension (DateTime dt) { // Instans-property public bool IsWeekend => dt.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday; public bool IsToday => dt.Date == DateTime.Today;
// Instans-metod public string ToSwedish() => dt.ToString("d MMMM yyyy", new System.Globalization.CultureInfo("sv-SE")); }
extension (DateTime) { // Statisk property på typen public static DateTime Tomorrow => DateTime.Today.AddDays(1); public static DateTime Yesterday => DateTime.Today.AddDays(-1);
// Statisk metod public static DateTime NextWeekday(DayOfWeek day) { var date = DateTime.Today.AddDays(1); while (date.DayOfWeek != day) date = date.AddDays(1); return date; } }}var today = DateTime.Today;Console.WriteLine(today.IsWeekend); // beror på dagenConsole.WriteLine(today.ToSwedish()); // t.ex. "30 september 2026"
Console.WriteLine(DateTime.Tomorrow); // morgondagens datumConsole.WriteLine(DateTime.NextWeekday(DayOfWeek.Monday)); // nästa måndagPraktiskt exempel — domänutökning
public class Order{ public int Id { get; init; } public string Customer { get; init; } public decimal Total { get; init; } public bool IsPaid { get; init; }}
public static class OrderExtensions{ extension (Order order) { public bool IsLarge => order.Total > 10_000; public bool IsUnpaid => !order.IsPaid; public string Summary => $"#{order.Id} — {order.Customer} — {order.Total:C}"; public decimal WithVat => order.Total * 1.25m; }
extension (IEnumerable<Order> orders) { public decimal TotalRevenue => orders.Sum(o => o.Total); public int UnpaidCount => orders.Count(o => o.IsUnpaid); }}var orders = new List<Order>{ new() { Id = 1, Customer = "Anna", Total = 15_000, IsPaid = true }, new() { Id = 2, Customer = "Björn", Total = 3_500, IsPaid = false }, new() { Id = 3, Customer = "Clara", Total = 8_200, IsPaid = false },};
foreach (var order in orders) Console.WriteLine($"{order.Summary} | Stor: {order.IsLarge} | inkl. moms: {order.WithVat:C}");
Console.WriteLine($"Total omsättning: {orders.TotalRevenue:C}");Console.WriteLine($"Obetald: {orders.UnpaidCount}");Output
#1 — Anna — 15 000,00 kr | Stor: True | inkl. moms: 18 750,00 kr#2 — Björn — 3 500,00 kr | Stor: False | inkl. moms: 4 375,00 kr#3 — Clara — 8 200,00 kr | Stor: False | inkl. moms: 10 250,00 krTotal omsättning: 26 700,00 krObetald: 2TL;DR
public static class MyExtensions{ // Instans-members extension (string s) { public bool IsEmail => s.Contains('@'); // property public string Upper() => s.ToUpper(); // metod }
// Statiska members extension (string) { public static string Empty => ""; // statisk property }}
// Används precis som inbyggda members"test@mail.com".IsEmail // True"hello".Upper() // HELLOstring.Empty // "" (men den finns ju redan...)Extension-properties fyller det sista hålet — nu kan du utöka vilken typ som helst med methods, properties och indexerare utan arv och utan att äga typen.