If you've been coding in C# for a while, you know it's a language that refuses to sit still. Every new version brings a fresh set of tools, and honestly, as someone who's been wrangling .NET for years, it's pretty exciting to see how much more expressive, concise, and just plain fun C# has become. Gone are the days of endless boilerplate for simple tasks!
Let's dive into some of the features that have genuinely changed how I approach C# development.
Streamlining Your Code: Less Ceremony, More Action!
One of the biggest shifts in recent C# versions has been a strong push towards reducing boilerplate. We're all busy, right? Why type more when you can say the same thing with less?
Top-Level Statements: Kickstarting Your Apps
Remember when even a simple "Hello World" console app needed a namespace, a class, and a static void Main(string[] args)? It felt a bit heavy for quick scripts or learning examples. Well, say hello to top-level statements!
Now, your entire program can look like this:
// Program.cs
Console.WriteLine("Hello, modern C#!");
// You can even use async/await directly
await Task.Delay(100);
Console.WriteLine("Waiting is over!");
This is fantastic for small utilities, Azure Functions, or just getting an idea off the ground without the cognitive load of a full class structure. It makes C# feel much more scripting-friendly.
Global Using Directives: De-cluttering Your Files
How many times have you scrolled past a dozen using statements at the top of every file? System, System.Collections.Generic, System.Linq, Microsoft.EntityFrameworkCore... the list goes on.
global using directives let you declare these common using statements once for your entire project. Just drop them into a file (often GlobalUsings.cs or Usings.cs), and they're available everywhere.
// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Microsoft.EntityFrameworkCore;
// ...and so on
The difference this makes in larger projects is immense. Your individual files become cleaner, focusing purely on the code within. No more endless scrolling past using blocks!
Embracing Immutability: Records & init Setters
Immutability is a huge win for robust, predictable software. When an object's state can't change after it's created, you eliminate entire classes of bugs related to unexpected side effects. C# has made it a first-class citizen with records and init setters.
Record Types: The Data-First Class
Records are essentially classes (or structs) designed for immutable data models. They come with a ton of goodies out of the box, like value-based equality, concise syntax, and non-destructive mutation (with expressions).
Imagine you're defining a Data Transfer Object (DTO) or an entity:
// Old way (class) - lots of boilerplate for immutability, equality, and ToString
public class Product
{
public int Id { get; }
public string Name { get; }
public decimal Price { get; }
public Product(int id, string name, decimal price)
{
Id = id;
Name = name;
Price = price;
}
// Need to override Equals, GetHashCode, ToString manually...
}
// New way (record) - concise, immutable by default, value equality
public record Product(int Id, string Name, decimal Price);
That's right, a single line! Records are perfect for scenarios where you just need to represent data. They save you from writing mountains of boilerplate for Equals, GetHashCode, and ToString, and they promote a functional style of programming.
init Setters: Immutable Properties, Flexible Creation
Even with regular classes, init setters provide a powerful way to ensure properties can only be set during object initialisation, enforcing immutability after creation.
public class UserProfile
{
public int Id { get; init; } // Can only be set during object creation
public string Username { get; init; }
public DateTime RegisteredDate { get; init; } = DateTime.UtcNow; // Default value also works
// Usage
var user = new UserProfile { Id = 1, Username = "devfriend" };
// user.Id = 2; // Error: Init-only setter cannot be used here
}
This gives you the best of both worlds: object initialiser syntax for easy construction, and immutability once the object is fully formed. Super handy for configuration objects or read-only view models.
Smarter Decisions: Enhanced Pattern Matching
Pattern matching isn't new, but it's been steadily enhanced, making complex conditional logic incredibly readable and concise. It allows you to test an expression against various "patterns" and execute code based on a match.
switch Expressions and Property Patterns
Gone are the days of clunky switch statements with case and break. switch expressions are, well, expressions – they return a value, making them perfect for transformations. Combine that with property patterns, and you have some serious power.
// Imagine a DTO or some input object
public record OrderItem(string ItemType, int Quantity, decimal UnitPrice);
public decimal CalculateDiscount(OrderItem item) => item switch
{
// If ItemType is "Book" AND Quantity > 5, apply 10% discount
{ ItemType: "Book", Quantity: > 5 } => item.Quantity * item.UnitPrice * 0.10M,
// If ItemType is "Software" AND UnitPrice > 100, apply 5% discount
{ ItemType: "Software", UnitPrice: > 100 } => item.Quantity * item.UnitPrice * 0.05M,
// Any other case
_ => 0M
};
// Even simpler: type patterns
public string GetShapeInfo(object shape) => shape switch
{
Circle c when c.Radius > 10 => $"Large Circle with radius {c.Radius}",
Circle c => $"Small Circle with radius {c.Radius}",
Rectangle r => $"Rectangle {r.Width}x{r.Height}",
_ => "Unknown shape"
};
This syntax is incredibly powerful for expressing intent clearly. Instead of nested if/else if statements, you get a clean, declarative structure that's easy to read and maintain.
Wrapping Up
These are just a few of my personal highlights from the recent evolutions of C#. From making quick scripts easier with top-level statements to building robust, immutable data models with records, and writing more expressive conditional logic with pattern matching, C# continues to empower developers to write cleaner, more maintainable code.
It's a fantastic time to be a C# developer, and I'm always excited to see what's next!
What new C# feature has made the biggest difference in your daily coding life? Let me know in the comments!


Top comments (0)