C# 12 is here, and it is a game-changer for developers! If you are a C# developer or just starting your programming journey, this version brings fresh features that will make your code cleaner, faster, and easier to maintain. In this blog, we will understand the most exciting features in C# 12 step by step. We will also explore the code samples and real-world examples.
Whether you are a student or an experienced developer, mastering C# 12 will significantly enhance your .net development skills. Let’s dive in!
If you want to test all C# and .Net code online, you can use dotnet Fiddle.
1. Primary Constructors for Classes
What It Is:
C# 12 now supports primary constructors for non-record classes, a feature previously limited to records.
Why It Matters:
It reduces boilerplate and makes your class definitions more concise.
Before (C# 11):
using System;
public class Program
{
public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
public static void Main()
{
var user = new User("Alex", 25);
Console.WriteLine($"Name: {user.Name}, Age: {user.Age}");
}
}
After (C# 12):
using System;
public class Program
{
public class User(string name, int age)
{
public string Name { get; } = name;
public int Age { get; } = age;
}
public static void Main()
{
var user = new User("Alex", 25);
Console.WriteLine($"Name: {user.Name}, Age: {user.Age}");
}
}
Output:
Name: Alexs, Age: 25
Real-World Use Case:
Perfect for lightweight data transfer classes in ASP.NET Core APIs or DTOs in microservices.
2. Collection Expressions
What It Is:
A new, cleaner syntax for initializing collections using [], inspired by array literals in other languages.
Why It Matters:
Reduces verbosity and enhances readability.
Before (C# 11):
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers = new List<int> { 1, 2, 3, 4 };
numbers.ForEach(n => Console.WriteLine(n));
}
}
After (C# 12):
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<int> numbers = [1, 2, 3, 4];
numbers.ForEach(n => Console.WriteLine(n));
}
}
Output:
1
2
3
4
Real-World Use Case:
Ideal for test data setup in unit tests or seeding demo data in ASP.NET Core apps.
3. Alias Any Type with using
What It Is:
Now you can create type aliases for any type, including generics!
Why It Matters:
Makes complex types easier to manage and improves code clarity.
Before (C# 11):
// Not possible with generics
After (C# 12):
using System;
// C# 12: Alias any type including generics
using IntList = System.Collections.Generic.List<int>;
using StringToIntMap = System.Collections.Generic.Dictionary<string, int>;
public class Program
{
public static void Main()
{
IntList numbers = [10, 20, 30];
StringToIntMap ages = new()
{
["Alice"] = 30,
["Bob"] = 25,
["Alex"] = 20
};
Console.WriteLine("Numbers:");
numbers.ForEach(Console.WriteLine);
Console.WriteLine("\nAges:");
foreach (var kvp in ages)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
}
}
Output:
Numbers:
10
20
30
Ages:
Alice: 30
Bob: 25
Alex: 20
Real-World Use Case:
Simplifies code in domain-driven design (DDD) where generic repositories and service patterns are used.
4. Default Parameters in Lambdas
What It Is:
Lambdas can now have default values for parameters.
Why It Matters:
Improves flexibility and reduces overloads or wrapper methods.
Before (C# 11):
using System;
public class Program
{
public static void Main()
{
Func<string?, string> greet = name =>
{
if (string.IsNullOrEmpty(name))
name = "Guest";
return $"Hello, {name}";
};
Console.WriteLine(greet(null));
Console.WriteLine(greet("Alice"));
}
}
After (C# 12):
using System;
delegate string Greet(string name = "Guest");
public class Program
{
public static void Main()
{
Greet greet = (string name = "Guest") => $"Hello, {name}";
Console.WriteLine(greet()); // Uses default
Console.WriteLine(greet("Alice")); // Overrides default
}
}
Output:
Hello, Guest
Hello, Alice
Real-World Use Case:
Useful in functional-style utilities and command-line tools where optional parameters are common.
5. ref readonly Parameters
What It Is:
Enhancement that allows parameters to be passed by reference and marked readonly.
Why It Matters:
Improves performance without risking mutation.
Before (C# 11):
You had to choose between in (readonly) and ref (mutable), and ref readonly was not allowed for method parameters.
After (C# 12):
using System;
public class Program
{
public static void Main()
{
string message = "Hello world";
PrintLength(ref message);
}
static void PrintLength(ref readonly string text)
{
Console.WriteLine(text.Length);
}
}
Output:
11
Real-World Use Case:
Great for performance-critical apps like game engines or high-throughput APIs where large structs or strings are passed frequently.
Conclusion: Why C# 12 Is a Must-Learn
C# 12 introduces powerful features that simplify your code and boost the performance of your application. Whether you’re building web apps, APIs, or desktop tools, these features will make your life easier. Start applying these features in your next .NET project and experience the difference.
Also Read,
- 5 Powerful Types of Constructors in C#: Simplify Your Code!
- Mastering Pattern Matching in C#
- SOLID Principles in C#