1. Primary Constructors for Classes
Primary constructors provide a concise way to declare constructors directly in the class declaration. This feature simplifies the syntax for creating classes with properties that need to be initialized through constructor parameters.
Example: Primary Constructors for Classes
public class Person(string firstName, string lastName)
{
public string FirstName { get; } = firstName;
public string LastName { get; } = lastName;
public void Display()
{
Console.WriteLine($"Name: {FirstName} {LastName}");
}
}
class Program
{
static void Main()
{
var person = new Person("John", "Doe");
person.Display(); // Output: Name: John Doe
}
}
- Explanation: The class
Person
defines its primary constructor inline with the class declaration. The constructor parametersfirstName
andlastName
are automatically assigned to the propertiesFirstName
andLastName
using property initializers. This syntax reduces boilerplate code.
2. Collection Literals
Collection Literals offer a more flexible and concise syntax for initializing collections. This allows you to initialize collections directly with elements in a simpler, cleaner manner.
Example: Collection Literals
using System.Collections.Generic;
class Program
{
static void Main()
{
var numbers = [1, 2, 3, 4, 5]; // Collection literal for List<int>
Console.WriteLine(string.Join(", ", numbers)); // Output: 1, 2, 3, 4, 5
var names = new List<string> { "Alice", "Bob", "Charlie" }; // Traditional syntax
Console.WriteLine(string.Join(", ", names)); // Output: Alice, Bob, Charlie
}
}
- Explanation: The collection literal syntax
[1, 2, 3, 4, 5]
simplifies the creation of aList<int>
. This shorthand reduces verbosity when initializing collections with predefined elements, though the traditionalList<string>
initialization is still available.
3. Readonly Members for Records
Records are designed to be immutable by default, but readonly members add another layer of immutability, ensuring that specific members can't be modified after the record is created, even if the record itself is not strictly immutable.
Example: Readonly Members for Records
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public readonly DateTime BirthDate; // Readonly member
public Person(string firstName, string lastName, DateTime birthDate)
{
FirstName = firstName;
LastName = lastName;
BirthDate = birthDate;
}
public void Display()
{
Console.WriteLine($"Name: {FirstName} {LastName}, BirthDate: {BirthDate.ToShortDateString()}");
}
}
class Program
{
static void Main()
{
var person = new Person("John", "Doe", new DateTime(1990, 1, 1));
person.Display(); // Output: Name: John Doe, BirthDate: 1/1/1990
}
}
- Explanation: The
BirthDate
member is declared asreadonly
, meaning it can only be set in the constructor and cannot be modified afterward. This ensures that certain properties remain immutable throughout the lifetime of the record, even if other members might be settable viainit
.
4. Default Interface Methods Enhancements
C# introduced default interface methods in C# 8, allowing interfaces to provide default implementations. Enhancements in newer versions of C# expand this feature, supporting more complex behaviors like overriding default methods in derived interfaces or classes.
Example: Default Interface Methods Enhancements
public interface ILogger
{
void Log(string message)
{
Console.WriteLine($"Default Log: {message}");
}
void Error(string message)
{
Console.WriteLine($"Default Error: {message}");
}
}
public class CustomLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Custom Log: {message}");
}
}
public class AdvancedLogger : CustomLogger, ILogger
{
// Inheriting default Error method from ILogger
}
class Program
{
static void Main()
{
ILogger logger = new CustomLogger();
logger.Log("Hello, World!"); // Output: Custom Log: Hello, World!
ILogger advancedLogger = new AdvancedLogger();
advancedLogger.Error("An error occurred"); // Output: Default Error: An error occurred
}
}
- Explanation: In this example, the
ILogger
interface provides default implementations for bothLog
andError
methods. TheCustomLogger
class overrides theLog
method, while theAdvancedLogger
class inherits the defaultError
method from the interface. This demonstrates the flexibility and power of default interface methods in allowing default behavior while still permitting customization.
Summary
- Primary Constructors for Classes: A shorthand for defining constructors directly within class declarations. It reduces boilerplate code by allowing constructor parameters to be declared inline with the class.
- Collection Literals: A more concise syntax for initializing collections, allowing you to create lists or arrays with a simplified literal syntax.
- Readonly Members for Records: Ensures immutability for specific members of a record, preventing them from being modified after the record is created, even if other members remain modifiable via the
init
keyword. - Default Interface Methods Enhancements: Expands the capabilities of default interface methods by supporting more complex scenarios, such as overriding default methods and allowing richer behavior across interfaces and classes.