New in .NET 10.0 [5]: Extension Blocks in C# 14.0
With the new C# keyword Extension, developers can extend existing classes.
(Image: Pincasso/Shutterstock)
- Dr. Holger Schwichtenberg
The post-hoc extensibility of classes with additional methods has existed under the name Extension Methods since C# language version 3.0, which was released in 2007 along with .NET Framework 3.5. This is even possible if the classes have already been compiled elsewhere, such as the libraries provided by Microsoft in the .NET Framework.
However, with Extension Methods, you can only add an instance method to existing classes. Thus, developers were forced to express constructs that were actually properties by name, unfortunately, as methods; see IsEmptyClassic() in the next listing.
Videos by heise
For this reason, there are some extension methods in the .NET class library that have names that one would intuitively expect as properties, including
Enumerable.Count()Queryable.Count()Enumerable.First()Enumerable.Last()
The following example code shows the classic extension methods:
public static class StringExtensionClassic
{
public static string TruncateClassic(this string s, int count)
{
if (s == null) return "";
if (s.Length <= count) return s;
return s.Substring(0, count) + "...";
}
public static bool IsEmptyClassic(this string s)
=> String.IsNullOrEmpty(s);
}
In C# 14.0, Microsoft now offers a generalized way to extend existing .NET classes with the new extension block keyword, called Extension Blocks or Extension Members.
The extension keyword must be part of a static, non-generic top-level class (i.e., not a nested class). After the extension keyword, you declare the type to be extended (Receiver). In the next listing, the receiver is the class System.String (alternatively abbreviated by the built-in type string). All methods and properties within the extension block then extend the receiver type named here. Currently, the following constructs can be used in these extension blocks (see next listing):
- Instance methods
- Static methods
- Instance properties
- Static properties
- Operators
Since there are no instance fields in extension blocks, you cannot extend the state of a class with extension blocks. You can only read and modify existing states (provided the type is mutable).
An extension block can contain any number of extension members. A class can contain multiple extension blocks as well as classic extension methods and other static members. This allows developers to implement the new extension blocks in existing classes with classic extension methods. There can also be multiple classes with extension blocks for a receiver type.
The following code example shows the extensions for System.String with C# 14.0:
public static class MyExtensions
{
// NEU in C# 14.0: // NEU in C# 14.0 Erweiterungsmitglieder (SchlĂĽsselwort extension)
extension(System.String s) // <-- Receiver (Zielklasse).
{
/// <summary>
/// Erweitern um eine Instanz-Methode (alternative Möglichkeit zur bisherigen Syntax)
/// </summary>
public string Truncate(int count)
{
if (s == null) return "";
if (s.Length <= count) return s;
return s.Substring(0, count) + string.Dots;
}
/// <summary>
/// NEU: Erweitern um eine Instanz-Eigenschaft nur mit Getter
/// </summary>
public bool IsEmpty => String.IsNullOrEmpty(s);
/// <summary>
/// NEU: Erweitern um eine Instanz-Eigenschaft mit Getter und Setter
/// </summary>
public int Size
{
get { return s.Length; }
set
{
// Neuzuweisung geht nicht; Da Strings immutable sind, funktioniert die Setter-Logik so nicht!!!
if (value < s.Length) s = s.Substring(0, value);
if (value > s.Length) s = s + new string('.', value - s.Length);
}
}
/// <summary>
/// NEU: Erweitern um eine statische Methode
/// </summary>
public static string Create(int count, char c = '.')
{
return new string(c, count);
}
/// <summary>
/// NEU: Erweitern um eine statische Instanz-Eigenschaft
/// </summary>
public static string Dots => "...";
// NEU: Erweitern um eine OperatorĂĽberladung
public static string operator *(string str, int i) // OperatorĂĽberladung
{
return string.Concat(Enumerable.Repeat(str, i)); ;
}
// NEU: OperatorĂĽberladung als Extension und neu ist auch, dass man ++ ĂĽberladen kann
public void operator ++()
{
s = s + String.Dots; // Das funktioniert so nicht, da Strings immutable sind!!!
}
}
}
The following code shows the call of the extension methods for the classes String and List<int>:
public void Run()
{
CUI.Demo(nameof(CS14_ExtensionDemo) + ": String");
string s1 = "Hallo Holger";
Console.WriteLine($"Vorher: {s1}");
string s2 = s1.TruncateClassic(5);
Console.WriteLine($"Nach TruncateClassic(): {s1}"); // Hello...
Console.WriteLine($"IsEmptyClassic():{s2.IsEmptyClassic()}"); // false
string s3 = "Hallo Holger";
Console.WriteLine($"Vorher: {s3}");
string s4 = s3.Truncate(5);
Console.WriteLine($"Nach Truncate(): {s4}"); // Hello...
Console.WriteLine($"IsEmpty:{s4.IsEmpty}"); // false
string s5 = (s1 + "! ") * 3;
Console.WriteLine($"*3: {s5}"); // "Hallo Holger!Hallo Holger!Hallo Holger!"
string s6 = string.Create(5, '#');
Console.WriteLine($"string.Create(5, '#'): {s6}"); // "#####"
#region nicht möglich
CUI.H2("s1.Size = 5 --> das geht nicht, weil die Size Property versucht, die Zeichenkette neu zuzuweisen!");
// Das geht nicht, weil die Size Property versucht, die Zeichenkette neu zuzuweisen!
s1.Size = 5;
Console.WriteLine(s1); // "Hallo Holger" statt wie erwartet "Hallo"
s1++;
Console.WriteLine(s1); // "Hallo Holger" statt wie erwartet "Hallo Holger..."
#endregion
}
(afl)