1. Record Types
Record types were introduced in C# 9.0 as a way to create immutable types with built-in support for value-based equality. Records are particularly useful for defining simple data structures where immutability and equality based on the value of properties (rather than reference) are desired.
Example: Record Types
public record Person(string FirstName, string LastName);
class Program
{
static void Main()
{
var person1 = new Person("John", "Doe");
var person2 = new Person("John", "Doe");
Console.WriteLine(person1 == person2); // Output: True (value-based equality)
Console.WriteLine(person1); // Output: Person { FirstName = John, LastName = Doe }
}
}
- Explanation: In this example,
Personis a record type with two properties,FirstNameandLastName. The equality comparison (==) checks the values of these properties, not their references. The defaultToStringmethod also provides a string representation of the record's values.
2. Init-Only Properties
Init-only properties, introduced in C# 9.0, allow you to create properties that can only be set during object initialization. This ensures that the property values remain immutable after the object is constructed.
Example: Init-Only Properties
public class Car
{
public string Make { get; init; }
public string Model { get; init; }
}
class Program
{
static void Main()
{
var car = new Car { Make = "Toyota", Model = "Corolla" };
// car.Make = "Honda"; // Error: Cannot modify an init-only property
Console.WriteLine($"{car.Make} {car.Model}"); // Output: Toyota Corolla
}
}
- Explanation: The
Carclass hasMakeandModelproperties that areinit-only. They can only be set during initialization and are immutable afterward.
3. Top-Level Programs
Top-level programs, introduced in C# 9.0, simplify the entry point of a console application by allowing you to omit the Main method and directly write code at the top level.
Example: Top-Level Programs
using System;
Console.WriteLine("Hello, World!");
- Explanation: In this example, there’s no
Mainmethod. TheConsole.WriteLinestatement is at the top level, and the compiler automatically generates theMainmethod behind the scenes. This is particularly useful for small or simple programs.
4. With Expressions
With expressions in C# 9.0 allow you to create a copy of an object with modifications to some of its properties. This is particularly useful for records and immutable types.
Example: With Expressions
public record Person(string FirstName, string LastName);
class Program
{
static void Main()
{
var person1 = new Person("John", "Doe");
var person2 = person1 with { LastName = "Smith" };
Console.WriteLine(person1); // Output: Person { FirstName = John, LastName = Doe }
Console.WriteLine(person2); // Output: Person { FirstName = John, LastName = Smith }
}
}
- Explanation: The
withexpression creates a newPersonobject,person2, by copyingperson1and modifying theLastNameproperty. The originalperson1remains unchanged.
5. Enhanced Pattern Matching
C# 9.0 introduces enhancements to pattern matching, including new patterns such as relational patterns, logical patterns, and more. These patterns make it easier to express complex conditions in a concise manner.
Example: Enhanced Pattern Matching
class Program
{
static string ClassifyNumber(int number) => number switch
{
< 0 => "Negative",
0 => "Zero",
> 0 and <= 10 => "Small Positive",
> 10 => "Large Positive",
_ => "Unknown"
};
static void Main()
{
Console.WriteLine(ClassifyNumber(-5)); // Output: Negative
Console.WriteLine(ClassifyNumber(5)); // Output: Small Positive
Console.WriteLine(ClassifyNumber(15)); // Output: Large Positive
}
}
- Explanation: This example uses relational patterns (
< 0,> 0 and <= 10) and logical patterns (and) to classify a number. The enhanced pattern matching makes the switch expression more expressive and readable.
6. Covariant Returns
Covariant returns, introduced in C# 9.0, allow an overriding method to return a more derived type than the method it overrides. This provides more flexibility and type safety in object-oriented design.
Example: Covariant Returns
public class Animal
{
public virtual Animal GetAnimal() => new Animal();
}
public class Dog : Animal
{
public override Dog GetAnimal() => new Dog(); // Covariant return
}
class Program
{
static void Main()
{
Dog dog = new Dog();
Animal animal = dog.GetAnimal(); // This returns a Dog, but can be assigned to an Animal variable
Console.WriteLine(animal.GetType().Name); // Output: Dog
}
}
- Explanation: The
Dogclass overrides theGetAnimalmethod of theAnimalclass and returns a more derived type (Dog). This is an example of covariant return types, where the overridden method can return a more specific type than the base method.
Summary
- Record Types: Introduce immutable types with value-based equality, making it easier to work with data-centric objects that should not change after creation.
- Init-Only Properties: Allow properties to be set only during object initialization, ensuring that they remain immutable afterward.
- Top-Level Programs: Simplify the syntax for small programs by allowing code to be written directly at the top level, without the need for a
Mainmethod. - With Expressions: Enable the creation of new objects by copying existing ones and modifying specific properties, particularly useful for immutable types.
- Enhanced Pattern Matching: Extend pattern matching capabilities with relational and logical patterns, making it easier to express complex conditions concisely.
- Covariant Returns: Allow overriding methods to return more derived types, providing more flexibility in method return types while maintaining type safety.