DEV Community

Cover image for How to Write Clean C# code? 5 Tips that will save you hours of programming
ByteHide
ByteHide

Posted on • Edited on • Originally published at bytehide.com

How to Write Clean C# code? 5 Tips that will save you hours of programming

Programming isn't always as simple as it seems; sometimes it requires you to write code that might not be quite as straightforward or easy to use as you'd like. You might also need to edit other people's messy code from time to time if you work in a company where developers aren't the cleanest coders around, and that can prove to be rather problematic and annoying, especially if you don't know how to do it properly yourself.

As you already know  - and for those who don't know -  I'm creating a series of articles exclusively for Writing Clean C# Code. So let's go see new ways to save headaches.


Limiting function arguments

It is very important to limit the number of parameters that a function has. Remember: The simpler, the better. The problem comes when a function has more than 3 arguments, because it can cause many problems in understanding what it does.

Bad way:

public void ProtectApplication(string path, string configurationPath, string outPutPath, CancellationToken cancellationToken)
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Good way:

public class ProtectionConfig
{
    public string Path { get; set; }
    public string ConfigurationPath { get; set; }
    public string OutPutPath { get; set; }
    public CancellationToken cancellationToken { get; set; }
}

var config = new ProtectionConfig
{
    Path = "./app.exe",
    ConfigurationPath = "./shield.config.json",
    OutPutPath = "./app_protected.exe",
    CancellationToken = source.Token;
};

public void ProtectApplication(ProtectionConfig config)
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

The best option is to use 1 or 2 arguments. Can you use more? Of course you can, but only if you are aware that in a couple of months you will have to figure out what is supposed to do what. The ideal would be to group when there are 3 or more.

If you have more than 2 arguments in your function, it means that function is doing too many things.


Name the functions with what they do

Another bad practice is to name functions with incorrect or incomplete names. Just by reading the name of the function we should know almost 100% of what EXACTLY it does, otherwise this only causes confusion.

Bad way:

public class SlackNotification
{
    //...

    public void Handle()
    {
        SendMessage(this._to, this._files, this._body);
    }
}

var message = new SlackNotification(...);
// What is this? A handle for the message? Are we writing to a file now?
message.Handle();
Enter fullscreen mode Exit fullscreen mode

Good way:

public class SlackNotification
{
    //...

    public void Send()
    {
        SendMessage(this._to, this._files, this._body);
    }
}

var message = new SlackNotification(...);
// Clear and obvious
message.Send();
Enter fullscreen mode Exit fullscreen mode

The best recommendation is to spend a little more time thinking of a name that is very descriptive for the function. If you don't do this good practice - just to save a couple of seconds in defining a good name, you will waste a lot more time in the future reading those lines of code and trying to understand what they do.


Never save unused code

This bad practice reminds me of when we keep or collect "stuff" at home but never make use of it. This is the same, if you have code that is not used, delete it. There is no point in having it taking up space and bothering you.

Bad way:

public void OldRequestMethod(string url)
{
    // ...
}

public void NewRequestMethod(string url)
{
    // ...
}

var request = NewRequestMethod(requestUrl);
SlackChannel("bytehide", request, "get users");
Enter fullscreen mode Exit fullscreen mode

Good way:

public void RequestMethod(string url)
{
    // ...
}

var request = RequestMethod(requestUrl);
SlackChannel("bytehide", request, "get users");
Enter fullscreen mode Exit fullscreen mode

Remember: Don't bite off more than you can chew. If you haven't used that code, there's no point in keeping it there. You can still check it again in the version history in case you ever need it.


One level of abstraction per function

If you are developing and you find that a function does not have only one level of abstraction, but has several, this means that the function is doing too many things by itself.

This bad practice, apart from in the future generating questions like: What is this? What exactly does it do? It makes it difficult to reuse code because it does many things at the same time.

Bad way:

public string ParseBetterJSAlternative(string code)
{
    var regexes = [
        // ...
    ];

    var statements = explode(" ", code);
    var tokens = new string[] {};
    foreach (var regex in regexes)
    {
        foreach (var statement in statements)
        {
            // ...
        }
    }

    var ast = new string[] {};
    foreach (var token in tokens)
    {
        // lex...
    }

    foreach (var node in ast)
    {
        // parse...
    }
}
Enter fullscreen mode Exit fullscreen mode

Good way:

class Tokenizer
{
    public string Tokenize(string code)
    {
        var regexes = new string[] {
            // ...
        };

        var statements = explode(" ", code);
        var tokens = new string[] {};
        foreach (var regex in regexes)
        {
            foreach (var statement in statements)
            {
                tokens[] = /* ... */;
            }
        }

        return tokens;
    }
}

class Lexer
{
    public string Lexify(string[] tokens)
    {
        var ast = new[] {};
        foreach (var token in tokens)
        {
            ast[] = /* ... */;
        }

        return ast;
    }
}

class BetterJSAlternative
{
    private string _tokenizer;
    private string _lexer;

    public BetterJSAlternative(Tokenizer tokenizer, Lexer lexer)
    {
        _tokenizer = tokenizer;
        _lexer = lexer;
    }

    public string Parse(string code)
    {
        var tokens = _tokenizer.Tokenize(code);
        var ast = _lexer.Lexify(tokens);
        foreach (var node in ast)
        {
            // parse...
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This, apart from leaving the code cleaner, will allow you to reuse code and test it (which you could not do properly before due to the large amount of things the function does).


Never write in global functions

This bad practice consists of "contaminating" the globals. This is not a good thing because you could have problems and even crash with another library. Any user could run into this problem and would not realize it until the exception occurred.

Bad way:

public Dictionary<string, string> Config()
{
    return new Dictionary<string,string>(){
        ["product"] = "shield"
    };
}
Enter fullscreen mode Exit fullscreen mode

Good way:

class Configuration
{
    private Dictionary<string, string> _configuration;

    public Configuration(Dictionary<string, string> configuration)
    {
        _configuration = configuration;
    }

    public string[] Get(string key)
    {
        return _configuration.ContainsKey(key) ? _configuration[key] : null;
    }
}
Enter fullscreen mode Exit fullscreen mode

After this, we load the configuration and when it is ready, we create an instance of the Configuration class.

var configuration = new Configuration(new Dictionary<string, string>() {
    ["product"] = "shield"
});
Enter fullscreen mode Exit fullscreen mode

Now yes, in the application, we should use one instance of Configuration


These ways are a bit more advanced than the ones in the previous article, but they are still simple - but very useful. As I said, the number of ways to write cleaner code in C# is more infinite than the universe. That's why I'm creating a list of Cleaner C# Code, and yes, I recommend you check it out.

If you are interested, don't forget to follow us so you don't miss any news!

Top comments (0)