DEV Community

Cover image for These 5 C# Guidelines (Revealed by a SENIOR DEVELOPER) will Change your Coding Style
Dotnetsafer
Dotnetsafer

Posted on • Originally published at blog.dotnetsafer.com

These 5 C# Guidelines (Revealed by a SENIOR DEVELOPER) will Change your Coding Style

Learning how to code can be challenging, but following the advice of more experienced developers can help you master the fundamentals of coding quickly and easily. These five C# guidelines, revealed by a senior developer, will change the way you write your code; this article will show you how to start coding like a pro today!

These guidelines are provided by Milan Jovanović, Senior Software Engineer at HTEC Group, a huge technology company that has raised a whopping $140M in its latest round of investment alone!


Define temporary variables in LINQ "Query" syntax

This first guideline, Milan comments that there is the possibility of defining temporary variables in LINQ "Query" syntax. 

It is curious because he has been talking to developers and many of them were completely unaware of this feature. And for this reason he has decided to explain it:

"Using the let keyword, you can define a temporary value in your LINQ queries. You can use it for further calculation or return this value as a result."

from user in _dbContext.Set<User>().AsNoTracking()
where user.Id == userId
let name = $"{user.FirstName} {user.LastName}"
let hasFirstName = !string.IsNullOrEmpty(user.FirstName)
let hasLastName = !string.IsNullOrEmpty(user.LastName)
select new
{
    user.Id,
    Name = name,
    ProfileComplete = hasFirstName && hasLastName
}
Enter fullscreen mode Exit fullscreen mode

Another point he makes is that when writing EF Core (Entity) queries, the clause used (let) is also translated into proper SQL.

In addition, it advises to test it and inspect the generated SQL:

"Not everything is supported like with in-memory LINQ."

📚Here you can read his explanation: Define temporary variables in LINQ "Query" syntax


Switch statement to compute a value

He has also decided to share a best practice when writing clean code in C#. I personally have seen quite a few cases like this and that's why I recommend you to check it out.

Milan tells us that from C# 8 onwards you can use switch expressions to replace the switch instruction.

Bad way:

switch (DateTime.Now.DayOfWeek)
{
    case DayOfWeek.Monday:
    case DayOfWeek.Tuesday:
    case DayOfWeek.Wednesday:
    case DayOfWeek.Thursday:
    case DayOfWeek.Friday:
        return "Not Weekend";
    case DayOfWeek.Saturday:
    case DayOfWeek.Sunday:
        return "Weekend";
    default:
        throw new ArgumentOutOfRangeException();
}
Enter fullscreen mode Exit fullscreen mode

Good way:

DateTime.Now.DayOfWeek switch
{
    not (DayOfWeek.Saturday or DayOfWeek.Sunday) => "Not Weekend",
    DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
    _ => throw new ArgumentOutOfRangeException()
}
Enter fullscreen mode Exit fullscreen mode

In addition, he says that there is room for improvement:

"Also, beginning with C# 9, we can add logical pattern matching operators into the mix for even more flexibility."

📚Here you can read his explanation: Switch statement to compute a value


Create a lazy-thread-safe-Singleton implementation

Here the questions arise as to how to create a lazy-thread-safe-Singleton implementation.

According to Milan, there are a variety of ways to do this but you should always rely on locking to prevent simultaneous access to make the implementation for thread-safe.

Milan warns that this practice would require a fairly advanced knowledge of locking mechanisms.

"However, we can utilize the Lazy class to "lazily" instantiate an instance of a class."

public sealed class Singleton
{
    private static readonly Lazy<Singleton> LazyInstance =
    new Lazy<Singleton>(() => new Singleton());
    private Singleton()
    { 
    }     
    public static Singleton Instance => LazyInstance.Value;
}
Enter fullscreen mode Exit fullscreen mode

Finally, he comments that concurrency is not important:

"Lazy is also thread-safe by default, so you don't have to "think" about concurrency."

📚Here you can read his explanation: Create a lazy-thread-safe-Singleton implementation


Create and use local functions

For those who do not know what a local function is, local functions allow you to declare a method inside the body of a previously defined method. This feature was added in C# 7 and Milan has decided to explain it in a clear way:

"Local functions are only visible inside the scope of their containing member. Usually, you would define and use them inside of another function."

public IEnumerable<string> CapitalizeFirstLetter(IEnumerable<string> enumerable)
{
    if (!enumerable.Any())
    {
        throw new ArgumentException("The sequence is empty.");
    }
    return enumerable.Select(CapitalizeFirstLetterLocal);
    static string CapitalizeFirstLetterLocal(string input) =>
       input switch
       {
            null or "" => throw new ArgumentNullException(nameof(input)),
            _ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
       };
}
Enter fullscreen mode Exit fullscreen mode

Another thing Milan adds is that there is also the possibility for local functions to be static, as long as they do not have access to the instance members.

"It is interesting to combine local functions with iterators.
Iterators are lazy be design. But you may want to perform an argument check eagerly, which is where local functions can be helpful."

📚Here you can read his explanation: Create and use local functions


Combine local functions with iterator blocks

Let's continue with the local functions! This time Milan wanted to share that there is the possibility to achieve lazy evaluation with iterator blocks while having an eager argument check.

"Notice that the local function uses the "yield return" statement, which executes when enumeration happens. If the iterator block is inside of "ReadFileLineByLine" directly, you will get the exception only when enumerating the results."

public IEnumerable<string> ReadFileLineByLine(string fileName)
{    
    if (string.IsNullOrEmpty(fileName))
    {
        throw new ArgumentNullException(nameof(fileName));
    }
return ReadFileLineByLineImpl();
IEnumerable<string> ReadFileLineByLineImpl()
    {
        foreach (var line in File.ReadAllLines(fileName))
        {
            yield return line;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This good practice makes it possible to detect exceptions before they occur:

"The exception will throw as soon as "ReadFileLineByLine" is invoked."

📚Here you can read his explanation: Combine local functions with iterator blocks


Thanks again to Milan Jovanović for sharing these guidelines and bringing value to the great and wonderful community of C# developers. If you liked them I would recommend you to follow him on Linkedin because he is always active and uploads a lot of valuable C# content!

Discussion (3)

Collapse
ahmad_butt_faa7e5cc876ea7 profile image
Ahmad

awesome!! .net core baby hooora!

bookmark'd

Collapse
kaylumah profile image
Max Hamulyák

This is a nice collection of tricks :)

Collapse
spatnaik197 profile image
sidhartha patnaik

Awesome...