DEV Community

Troy Miller
Troy Miller

Posted on

100 C# Tips I Wish I Knew Earlier (Part 1)

After 20+ years working with C#, I've collected small lessons that made a huge difference in how I write code.

Some of these saved me hours of debugging. Others improved performance or made my code cleaner overnight.

Here are 25 practical C# tips I wish I knew earlier.


C# coding tips illustration with code snippets

Modern C# Features That Improve Your Code

1. Embrace Primary Constructors

I once refactored a class with multiple constructors doing the same thing. It was messy.

Primary constructors simplify everything:

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;
}
Enter fullscreen mode Exit fullscreen mode

Cleaner, shorter, and easier to maintain.


2. Use Collection Expressions

Initializing collections used to feel verbose.

List<int> numbers = [1, 2, 3, 4, 5];
Enter fullscreen mode Exit fullscreen mode

Less typing, more clarity.


3. Use nameof for Safer Refactoring

A typo once cost me hours.

Console.WriteLine(nameof(Person.Name));
Enter fullscreen mode Exit fullscreen mode

Now the compiler helps you catch mistakes.


4. Use Null-Coalescing for Safer Code

Null reference bugs are painful.

string name = person.Name ?? "Unknown";
Enter fullscreen mode Exit fullscreen mode

Simple and safe.


5. Pattern Matching = Cleaner Logic

Refactoring large conditionals becomes much easier:

if (person is { Age: > 18, Name: not null })
{
    Console.WriteLine($"{person.Name} is an adult.");
}
Enter fullscreen mode Exit fullscreen mode

6. Prefer String Interpolation

Readable and clean:

string message = $"Hello, {person.Name}!";
Enter fullscreen mode Exit fullscreen mode

7. Use Tuples Instead of Extra Classes

public (string, int) GetPersonInfo()
{
    return ("Alice", 30);
}
Enter fullscreen mode Exit fullscreen mode

Lightweight and efficient.


8. Use Local Functions for Better Structure

void Process()
{
    int Add(int a, int b) => a + b;
    Console.WriteLine(Add(1, 2));
}
Enter fullscreen mode Exit fullscreen mode

Keeps logic scoped and readable.


9. Use using var Instead of Nested Blocks

using var reader = new StreamReader("file.txt");
Enter fullscreen mode Exit fullscreen mode

Cleaner resource management.


10. Use IAsyncEnumerable for Streaming Data

public async IAsyncEnumerable<int> GetData()
{
    for (int i = 0; i < 5; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}
Enter fullscreen mode Exit fullscreen mode

Great for performance.


Code Quality & Clean Design

11. Use Records for Immutable Data

public record Person(string Name, int Age);
Enter fullscreen mode Exit fullscreen mode

No more boilerplate.


12. Nullable Reference Types Save You

string? name = null;
Enter fullscreen mode Exit fullscreen mode

Catch issues at compile time.


13. Switch Expressions > Switch Statements

string result = age switch
{
    > 18 => "Adult",
    _ => "Minor"
};
Enter fullscreen mode Exit fullscreen mode

14. Use with for Object Updates

var updated = person with { Age = 31 };
Enter fullscreen mode Exit fullscreen mode

15. File-Scoped Namespaces Reduce Noise

namespace MyApp;
Enter fullscreen mode Exit fullscreen mode

Cleaner files instantly.


16. Use required for Safer Models

public class Person
{
    public required string Name { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

17. Init-Only Properties for Immutability

public string Name { get; init; }
Enter fullscreen mode Exit fullscreen mode

18. Global Using Saves Repetition

global using System;
Enter fullscreen mode Exit fullscreen mode

19. CallerArgumentExpression for Debugging

public void Validate(bool condition, 
    [CallerArgumentExpression("condition")] string msg = "")
{
    if (!condition) throw new Exception(msg);
}
Enter fullscreen mode Exit fullscreen mode

20. Default Interface Methods

public interface ILogger
{
    void Log(string msg) => Console.WriteLine(msg);
}
Enter fullscreen mode Exit fullscreen mode

Performance & Advanced Tips

21. Use Span for High Performance

Span<int> numbers = stackalloc int[10];
Enter fullscreen mode Exit fullscreen mode

22. Use ArrayPool to Reduce GC Pressure

var pool = ArrayPool<int>.Shared;
var arr = pool.Rent(10);
pool.Return(arr);
Enter fullscreen mode Exit fullscreen mode

23. Use ValueTask for Lightweight Async

public ValueTask<int> GetValueAsync() => new(42);
Enter fullscreen mode Exit fullscreen mode

24. ConfigureAwait for Better Performance

await Task.Delay(100).ConfigureAwait(false);
Enter fullscreen mode Exit fullscreen mode

25. Use Stopwatch to Measure Performance

var sw = Stopwatch.StartNew();
// code
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

These small improvements compound over time.

They made my code:

  • Cleaner
  • Faster
  • Easier to maintain

Top comments (0)