1. Required Properties
In C# 11, the required
keyword was introduced to enforce that certain properties must be initialized during object creation. This helps ensure that objects are always created in a valid state without relying on constructors alone.
Example: Required Properties
public class Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
// The required properties must be initialized during object creation
var person = new Person { FirstName = "John", LastName = "Doe", Age = 30 };
Console.WriteLine($"{person.FirstName} {person.LastName}, Age: {person.Age}");
// var invalidPerson = new Person { Age = 30 }; // This would cause a compile-time error
}
}
- Explanation: The
required
keyword ensures thatFirstName
andLastName
are always initialized. If these properties are not set during object creation, the compiler throws an error, ensuring the object is never created in an invalid state.
2. Generic Math Support
C# 11 introduces Generic Math Support, allowing you to use mathematical operators like +
, -
, *
, and /
with generic types that are constrained to numeric types. This makes it possible to write more flexible and reusable algorithms that work across different numeric types.
Example: Generic Math Support
public class Calculator<T> where T : INumber<T>
{
public T Add(T a, T b) => a + b;
public T Multiply(T a, T b) => a * b;
}
class Program
{
static void Main()
{
var intCalculator = new Calculator<int>();
Console.WriteLine(intCalculator.Add(3, 4)); // Output: 7
Console.WriteLine(intCalculator.Multiply(3, 4)); // Output: 12
var doubleCalculator = new Calculator<double>();
Console.WriteLine(doubleCalculator.Add(2.5, 3.5)); // Output: 6
Console.WriteLine(doubleCalculator.Multiply(2.5, 3.5)); // Output: 8.75
}
}
- Explanation: In this example, the
Calculator<T>
class can perform addition and multiplication on any type that implements theINumber<T>
interface, such asint
anddouble
. The newINumber<T>
interface, part of theSystem.Numerics
namespace, enables this generic math support.
3. Static Abstract Members in Interfaces
C# 11 introduces static abstract members in interfaces, allowing interfaces to declare static members that must be implemented by concrete types. This is useful in generic programming scenarios where you want to ensure that implementing types provide specific static methods.
Example: Static Abstract Members in Interfaces
public interface IFactory<T>
{
static abstract T Create();
}
public class Car : IFactory<Car>
{
public static Car Create() => new Car { Make = "Toyota", Model = "Corolla" };
public string Make { get; set; }
public string Model { get; set; }
}
public class Truck : IFactory<Truck>
{
public static Truck Create() => new Truck { Make = "Ford", Model = "F-150" };
public string Make { get; set; }
public string Model { get; set; }
}
class Program
{
static void Main()
{
var car = Car.Create();
var truck = Truck.Create();
Console.WriteLine($"{car.Make} {car.Model}"); // Output: Toyota Corolla
Console.WriteLine($"{truck.Make} {truck.Model}"); // Output: Ford F-150
}
}
- Explanation: The
IFactory<T>
interface declares astatic abstract T Create()
method. BothCar
andTruck
classes implement this method, ensuring that each type can be created using the staticCreate
method. This feature allows more powerful and flexible designs in generic programming.
4. UTF-8 String Literals
C# 11 introduces UTF-8 string literals, which allow string literals to be represented as UTF-8 encoded bytes using the u8
suffix. This feature is useful for optimizing performance in scenarios where you work with UTF-8 data directly, such as network protocols or file I/O.
Example: UTF-8 String Literals
class Program
{
static void Main()
{
ReadOnlySpan<byte> utf8Text = "Hello, UTF-8!"u8;
foreach (var b in utf8Text)
{
Console.Write($"{b:X2} "); // Outputs UTF-8 encoded bytes in hex
}
// Output: 48 65 6C 6C 6F 2C 20 55 54 46 2D 38 21
}
}
- Explanation: The
u8
suffix converts the string"Hello, UTF-8!"
into aReadOnlySpan<byte>
containing its UTF-8 encoded bytes. This is useful in performance-sensitive scenarios where handling raw UTF-8 bytes is needed, such as low-level network communication.
Summary
- Required Properties: Introduces the
required
keyword to enforce that certain properties must be initialized during object creation, ensuring that objects are always in a valid state. - Generic Math Support: Adds support for using mathematical operators with generics constrained to numeric types, enabling more flexible and reusable numeric algorithms.
- Static Abstract Members in Interfaces: Allows static members to be declared in interfaces, ensuring that implementing types provide specific static methods, making generic programming more powerful.
- UTF-8 String Literals: Introduces the
u8
suffix for string literals to represent them as UTF-8 encoded bytes, improving performance in scenarios where UTF-8 data needs to be handled directly.