A practical guide to writing cleaner, safer, and more modern C# code.
Introduction
The .NET ecosystem evolves rapidly, and with every release Microsoft introduces features that make development easier, cleaner, and more efficient. Unfortunately, many developers continue writing code the same way they did years ago, missing out on improvements that can simplify their daily work.
In this article, we'll explore 10 hidden (or underused) .NET features that every developer should know. These aren't gimmicks—they're practical tools you can start using today in real-world applications.
Whether you're building ASP.NET Core APIs, desktop applications, or cloud services, these features will help you write better code.
1. Collection Expressions
One of the cleanest additions to modern C# is Collection Expressions.
Instead of writing:
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
You can now simply write:
int[] numbers = [1, 2, 3, 4, 5];
It also works with lists:
List<string> names =
[
"John",
"Emma",
"David"
];
Why it matters
- Less typing
- Cleaner syntax
- Easier to read
- Perfect for initialization
2. Primary Constructors
Dependency Injection becomes much cleaner.
Old style:
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
}
Modern C#:
public class UserService(IUserRepository repository)
{
}
Benefits
- Removes boilerplate
- Cleaner classes
- Easier maintenance
3. Required Members
Forget assigning required properties? The compiler now helps you.
public class Employee
{
public required string Name { get; set; }
public required string Email { get; set; }
}
Now this won't compile:
var employee = new Employee();
Correct:
var employee = new Employee
{
Name = "Abinash",
Email = "abinash@example.com"
};
Benefits
- Compile-time safety
- Prevents null issues
- Better models
4. TimeProvider
Instead of using:
DateTime.Now
Use:
TimeProvider.System.GetLocalNow();
Why?
Because DateTime.Now cannot easily be mocked during testing.
TimeProvider can.
Perfect for:
- Unit Tests
- Scheduling
- Background Services
5. Keyed Dependency Injection
Need multiple implementations?
Example:
builder.Services.AddKeyedScoped<IPaymentService, PaypalService>("paypal");
builder.Services.AddKeyedScoped<IPaymentService, StripeService>("stripe");
Inject the one you need.
No more huge switch statements.
6. Rate Limiting Middleware
Protect your API from abuse.
Enable:
builder.Services.AddRateLimiter();
app.UseRateLimiter();
Example:
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("api", limiterOptions =>
{
limiterOptions.PermitLimit = 10;
limiterOptions.Window = TimeSpan.FromMinutes(1);
});
});
Benefits:
- Stops spam
- Prevents brute-force attacks
- Protects servers
7. File-Scoped Namespaces
Old style:
namespace MyProject
{
public class User
{
}
}
Modern:
namespace MyProject;
public class User
{
}
Cleaner.
Less indentation.
Much easier to navigate.
8. Global Using Directives
Instead of repeating:
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
In every file...
Create:
GlobalUsings.cs
global using Microsoft.EntityFrameworkCore;
global using Microsoft.AspNetCore.Mvc;
global using System.Text.Json;
Now every file automatically has access.
9. Pattern Matching
Old:
if (person is Employee)
{
}
else if(person is Student)
{
}
Modern:
return person switch
{
Employee e => e.Name,
Student s => s.Name,
_ => "Unknown"
};
Much cleaner.
Much more readable.
10. Minimal APIs
Creating APIs has never been easier.
var app = builder.Build();
app.MapGet("/users", () =>
{
return Results.Ok(users);
});
app.Run();
Perfect for:
- Microservices
- Internal APIs
- Prototypes
- Serverless applications
Bonus Features Worth Exploring
- Span
- Memory
- FrozenDictionary
- Source Generators
- Native AOT
- Generic Math
- Interceptors
- UTF-8 String Literals
- CallerArgumentExpression
- ConfigureAwaitOptions
Final Thoughts
The .NET platform keeps getting better with every release, but many of its best improvements go unnoticed because developers stick to familiar patterns.
By adopting features like Collection Expressions, Primary Constructors, Required Members, TimeProvider, and Minimal APIs, you can write code that's cleaner, safer, and easier to maintain.
The best part? You don't need to learn an entirely new framework—just start incorporating these features into your existing projects one at a time.
Small improvements compound over time, and modern C# gives you the tools to build applications that are both elegant and productive.
Happy coding! 🚀
If you enjoyed this article, let me know which hidden .NET feature you use the most—or which one you plan to try next!
Top comments (0)