1. Async Main
In C# 7.1 and later, the Main
method, which serves as the entry point for console applications, can be asynchronous. This allows you to use the async
and await
keywords directly within Main
, making it easier to handle asynchronous operations like I/O tasks.
Example: Async Main
using System;
using System.Threading.Tasks;
class Program
{
// Async Main method with Task return type
static async Task Main(string[] args)
{
Console.WriteLine("Starting async Main...");
// Asynchronously wait for 2 seconds
await Task.Delay(2000);
Console.WriteLine("Completed after 2 seconds.");
}
}
- Explanation: The
Main
method is marked asasync
and returns aTask
. Inside the method, we can useawait
to asynchronously perform operations, such as waiting for 2 seconds withTask.Delay
.
2. Default Literals
Default literals were introduced in C# 7.1 and simplify the syntax for specifying default values for types. Previously, when using the default
keyword, you had to specify the type (e.g., default(int)
), but with default literals, you can simply use default
.
Example: Default Literals
class Program
{
static void PrintDefaultValues<T>()
{
// Using default literal
T defaultValue = default;
Console.WriteLine($"Default value of {typeof(T)} is: {defaultValue}");
}
static void Main()
{
PrintDefaultValues<int>(); // Output: Default value of System.Int32 is: 0
PrintDefaultValues<string>(); // Output: Default value of System.String is:
PrintDefaultValues<bool>(); // Output: Default value of System.Boolean is: False
}
}
- Explanation: The
default
literal allows you to initialize variables with their default values in a much cleaner way. For example,default
forint
is0
, forbool
isfalse
, and for reference types likestring
, it'snull
.
3. In Parameters
The in
keyword in C# 7.2 and later allows you to pass parameters by reference while guaranteeing that they will not be modified. This is useful when you want to avoid the overhead of copying large value types (like structs) but still ensure that the original data remains unchanged.
Example: In Parameters
using System;
struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
// Method accepting an in parameter
public static int CalculateDistance(in Point point)
{
// point.X and point.Y are read-only inside this method
return (int)Math.Sqrt(point.X * point.X + point.Y * point.Y);
}
}
class Program
{
static void Main()
{
Point p = new Point(3, 4);
// Passing p by reference without allowing modification
int distance = Point.CalculateDistance(in p);
Console.WriteLine($"Distance: {distance}"); // Output: Distance: 5
}
}
- Explanation: The
in
parameter passes thePoint
struct by reference, avoiding the overhead of copying it. The method can read the data but cannot modify it. This guarantees both performance and immutability for the passed data.
4. Ref Structs and ReadOnly Structs
C# introduces ref structs and readonly structs to improve value type safety and efficiency.
- Ref Structs: These structs are allocated on the stack instead of the heap and can only be passed by reference, ensuring they don't escape the stack (i.e., they can't be boxed or used in async methods).
- Readonly Structs: These structs are immutable, meaning their fields cannot be modified after initialization.
Example: Ref Structs
ref struct SpanExample
{
private readonly Span<int> _span;
public SpanExample(Span<int> span)
{
_span = span;
}
public void SetValue(int index, int value)
{
_span[index] = value;
}
public int GetValue(int index) => _span[index];
}
class Program
{
static void Main()
{
Span<int> numbers = stackalloc int[3] { 1, 2, 3 };
SpanExample example = new SpanExample(numbers);
example.SetValue(1, 42);
Console.WriteLine(example.GetValue(1)); // Output: 42
}
}
- Explanation: The
SpanExample
struct is aref struct
, meaning it cannot be allocated on the heap or passed to async methods. It wraps around aSpan<int>
, a stack-allocated memory slice, and provides methods to interact with it.
Example: ReadOnly Structs
readonly struct ReadOnlyPoint
{
public int X { get; }
public int Y { get; }
public ReadOnlyPoint(int x, int y) => (X, Y) = (x, y);
// Read-only method
public int CalculateSquareDistance() => X * X + Y * Y;
}
class Program
{
static void Main()
{
ReadOnlyPoint point = new ReadOnlyPoint(3, 4);
int distanceSquared = point.CalculateSquareDistance();
Console.WriteLine($"Distance squared: {distanceSquared}"); // Output: Distance squared: 25
}
}
- Explanation: The
ReadOnlyPoint
struct is marked asreadonly
, ensuring that its fields cannot be modified once they are set. This guarantees immutability, making the struct safer and more predictable to use.
Summary
- Async Main: Allows the
Main
method to be asynchronous, supporting the use ofasync
andawait
for easier handling of asynchronous tasks. - Default Literals: Simplifies the usage of the
default
keyword by eliminating the need to specify the type. - In Parameters: Enables passing parameters by reference without allowing the method to modify them, optimizing performance for large structs while ensuring immutability.
- Ref Structs and ReadOnly Structs: Improve safety and performance by ensuring that
ref structs
are stack-allocated and cannot be used in async or heap-based contexts, and by ensuringreadonly structs
are immutable.