May 19, 2023 13 min read

Best Practices for C# - Coding Standards

This blog post outlines best practices for C# coding standards, covering class and variable naming, enums, and more. Improve your code consistency and readability with these tips.

Best Practices for C# - Coding Standards

As a programming language, C# offers a lot of flexibility and power, which makes it a popular choice for building a wide range of software applications. However, without a set of coding standards and guidelines, the resulting code can become difficult to read, maintain, and extend.

Following a set of best practices for coding standards can help address these issues by promoting consistency, clarity, and readability in code. By adhering to these practices, developers can also improve the maintainability, testability, and extensibility of their code.

Introduction

In this blog post, I will cover some fundamental best practices for C# coding standards. These practices cover areas such as class names, variable names, constants, abbreviations, no underscores, type names, interfaces, file names, namespaces, curly brackets, member variables, enums, and enum types.

While not an exhaustive list of all possible coding standards and guidelines, these practices represent widely-accepted and fundamental guidelines that can help you write clean, maintainable, and readable C# code. What's not to like here!?

"Following C# coding standards isn't just about consistency - it's about making your code more readable and maintainable for yourself and your team." - Christian Schou

By the end of this blog post, you should have a better understanding of the importance of following coding standards, as well as some practical tips for writing C# code that is consistent, easy to read, and maintainable. Let's get started! 💪

Class Names

Let's begin with some basics. When defining a class in C#, it's important to choose a name that is descriptive and easy to read. Class names should use PascalCase, which means that the first letter of each word is capitalized.

Ex. 1 - GetUserNameFromDatabase
Ex. 2 - PlacePurchaseOrderInApplicationDatabase

For example, consider the following class definitions using C#.

// Good:
public class CustomerOrder
{
    // ...
}

public class ShoppingCart
{
    // ...
}

// Bad:
public class CO
{
    // ...
}

public class SCart
{
    // ...
}

In the above code example, the CustomerOrder and ShoppingCart class names are easy to read and understand, while the CO and SCart class names are ambiguous and unclear.

Class names should also be nouns or noun phrases, rather than verbs or verb phrases. This helps make the purpose of the class clear and reduces confusion.

// Good:
public class CustomerOrder
{
    // ...
}

public class ShoppingCart
{
    // ...
}

// Bad:
public class CalculateTotal
{
    // ...
}

public class AddToCart
{
    // ...
}

In the above code examples, the CustomerOrder and ShoppingCart class names are nouns that describe the objects being modeled, while the CalculateTotal and AddToCart class names are verbs that describe actions to be performed.

Finally, avoid using abbreviations in class names, unless they are widely accepted and understood. If an abbreviation is used, it should be all uppercase.

// Good:
public class HttpRequest
{
    // ...
}

public class XmlDocument
{
    // ...
}

// Bad:
public class HTTPReq
{
    // ...
}

public class XMLDoc
{
    // ...
}

In the above code example, the HttpRequest and XmlDocument class names are clear and descriptive, while the HTTPReq and XMLDoc class names use abbreviations that may be unclear to other developers.

Following these guidelines for class names can make your code more readable and maintainable, and help ensure that other developers can quickly understand the purpose and functionality of your classes.

Variable Names and Identifiers

Variable names and identifiers should also be chosen carefully to make code more readable and maintainable. Here are my guidelines to follow:

Use descriptive names

Variable names should clearly indicate the purpose of the variable. This helps make the code easier to understand and reduces the risk of errors.

// Good:
int studentAge = 27;
string customerName = "Christian Schou";

// Bad:
int a = 27;
string b = "Christian Schou";

Use camelCase

In C#, variable names should use camelCase, which means the first letter of the first word is lowercase and the first letter of each subsequent word is capitalized.

// Good:
int studentAge = 27;
string customerName = "Christian Schou";

// Bad:
int StudentAge = 27;
string CustomerName = "Christian Schou";

Avoid abbreviations

Like with class names, it's best to avoid abbreviations in variable names unless they are widely accepted and understood.

// Good:
string customerName = "Christian Schou";

// Bad:
string custName = "Christian Schou";

Avoid using underscores

While underscores are valid characters for variable names, they should generally be avoided in favor of camelCase, as underscores can make variable names harder to read.

// Good:
int numberOfStudents = 50;

// Bad:
int number_of_students = 50;

Use meaningful identifiers

When declaring variables or other identifiers, it's important to choose a name that is meaningful and descriptive.

// Good:
int numberOfStudents = 50;

// Bad:
int n = 50;

Following these guidelines for variable names and identifiers can help make code more readable and maintainable, and reduce the risk of errors or confusion.

Constants

Constants are values that do not change during the execution of a program. They are defined using the const keyword in C# and are typically used to define values that are used throughout the program, such as mathematical constants or configuration settings. An example from my own experience is that often use them with message queue names for distributed applications.

When defining constants, it's important to follow these guidelines.

Use PascalCase

Like with class names, constants should use PascalCase to make them easily distinguishable from other identifiers in the code.

// Good:
public const int MaxItemCount = 100;
public const string AppName = "TwcApp";

// Bad:
public const int maxItemCount = 100;
public const string appName = "TwcApp";

Use meaningful names

Constant names should be meaningful and descriptive, just like variable names.

// Good:
public const int MaxItemCount = 100;

// Bad:
public const int X = 100;

Use constants for values that will not change

Constants should only be used for values that will not change during the execution of the program. If a value might change, it should be defined as a variable instead.

// Good:
public const int MaxItemCount = 100;

// Bad:
public const int CurrentItemCount = 0;

Use constants for values that are used multiple times

Constants should be used for values that are used multiple times throughout the program, to reduce the risk of errors and make it easier to update the value if necessary.

// Good:
public const int MaxItemCount = 100;
public const int DefaultPageSize = 10;

// Bad:
public const int MaxItemCount = 100;
public const int PageSize = 10;

By following these guidelines for constants, you can make your code more readable and maintainable, and reduce the risk of errors caused by magic numbers or other hard-coded values.

Abbreviations and Underscores

While abbreviations and underscores are technically allowed in C# identifiers, it's generally best to avoid them as they can make code harder to read and understand.

Avoid abbreviations

Unless an abbreviation is widely used and understood, it's best to avoid using them in identifiers.

// Good:
string customerName;

// Bad:
string custName;

Avoid underscores

While underscores are valid characters for identifiers, they can make code harder to read and understand. Instead, use camelCase for multi-word identifiers.

// Good:
int numberOfStudents;

// Bad:
int number_of_students;

Use abbreviations and underscores sparingly

In some cases, abbreviations and underscores may be necessary for brevity or to conform to an existing naming convention. In these cases, use them sparingly and only where they are necessary.

// Good:
string avgTemp;
string xmlFile;

// Bad:
string averageTemperature;
string XMLFile;

By avoiding unnecessary abbreviations and underscores, you can make your code more readable and easier to understand.

Type Names and Noun Class Names

When defining types and classes in C#, it's important to follow these guidelines.

Use PascalCase

Like with class names and constants, type names should use PascalCase to make them easily distinguishable from other identifiers in the code.

// Good:
public class Person { }

// Bad:
public class person { }

Use noun class names

Class names should be nouns, as they represent objects or concepts in your program.

// Good:
public class Car { }

// Bad:
public class Drive { }

Use singular nouns

Class names should use singular nouns, as they represent a single instance of an object or concept.

// Good:
public class Car { }

// Bad:
public class Cars { }

Avoid generic type names

Generic type names should be specific and descriptive, and avoid using generic names such as Object, List, or Array. This helps to avoid naming collisions and makes it easier to understand the purpose of the type.

// Good:
public class CustomerList { }

// Bad:
public class ObjectList { }

By following these guidelines for type names and noun class names, you can make your code more readable and easier to understand, and reduce the risk of naming collisions and confusion.

Interfaces

When defining interfaces in C#, it's important to follow these guidelines.

Use "I" prefix

Interface names should start with the letter "I" to make it clear that they are interfaces.

// Good:
public interface IShape { }

// Bad:
public interface Shape { }

Use noun interface names

Like with class names, interface names should be nouns, as they represent objects or concepts in your program.

// Good:
public interface IShape { }

// Bad:
public interface ICalculate { }

Use the single responsibility principle

Interfaces should follow the single responsibility principle and define only one cohesive set of related operations.

// Good:
public interface IShape
{
    double GetArea();
    double GetPerimeter();
}

// Bad:
public interface IShape
{
    void Draw();
    double GetArea();
}
💡
The single responsibility principle (SRP) is a fundamental concept in object-oriented programming that states that a class or interface should have only one responsibility, meaning that it should do only one thing and do it well. In the context of interfaces, this means that an interface should define only one cohesive set of related operations that are logically connected to each other.

By following these guidelines for interfaces, you can make your code more modular and easier to maintain, and reduce the risk of naming collisions and confusion.

File Names and Namespaces

When defining files and namespaces in C#, it's important to follow these guidelines.

Use descriptive file names

File names should be descriptive and reflect the purpose of the code in the file.

// Good:
Person.cs

// Bad:
File1.cs

Use PascalCase for file names

Like with class names, file names should use PascalCase to make them easily distinguishable from other identifiers in the code.

// Good:
Person.cs

// Bad:
person.cs

Namespaces should be used to group related classes and prevent naming collisions between classes in different parts of the codebase.

// Good:
namespace TechWithChristian.Members.Core
{
    public class Person { }
}

// Bad:
public class Person { }

Use dot notation for namespace

Namespace should follow dot notation to reflect the hierarchical structure of the code.

// Good:
namespace TechWithChristian.Members.Core
{
    public class Person { }
}

// Bad:
namespace TechWithChristian_Members_Core
{
    public class Person { }
}

By following these guidelines for file names and namespaces, you can make your code more organized, easier to find and navigate, and reduce the risk of naming collisions and confusion.

Curly Brackets

When using curly brackets to define code blocks in C#, it's important to follow these guidelines.

Use consistent indentation

Code blocks should be indented consistently, with each level of indentation represented by a fixed number of spaces or tabs. This helps to make the code more readable and easier to follow.

// Good:
if (condition)
{
    statement1;
    statement2;
}

// Bad:
if (condition)
{ statement1;
  statement2;
}

Use curly brackets even for single-line blocks

While it's technically possible to omit curly brackets for single-line code blocks, it's recommended to always include them for clarity and consistency.

// Good:
if (condition)
{
    statement1;
}

// Also good:
if (condition) statement1;

Use an opening curly bracket on the same line

In C#, the opening curly bracket for a code block should always appear on the same line as the corresponding statement, not on a new line.

// Good:
if (condition) {
    statement1;
}

// Bad:
if (condition)
{
    statement1;
}

By following these guidelines for curly brackets, you can make your code more consistent, readable, and maintainable, and reduce the risk of syntax errors and confusion.

Member Variables

When defining member variables in C#, it's important to follow these guidelines.

Use meaningful names

Like with other identifiers in your code, member variables should have meaningful names that reflect their purpose and usage in the code.

// Good:
public class Person
{
    private string firstName;
    private string lastName;
}

// Bad:
public class Person
{
    private string a;
    private string b;
}

Use PascalCase for member variable names

As with class and type names, member variable names should be written in PascalCase.

// Good:
public class Person
{
    private string FirstName;
    private string LastName;
}

// Bad:
public class Person
{
    private string firstname;
    private string lastname;
}

Avoid using public member variables

While it's possible to define public member variables in C#, it's generally considered bad practice because it can expose implementation details and make it harder to maintain the code.

// Bad:
public class Person
{
    public string FirstName;
    public string LastName;
}

// Good:
public class Person
{
    private string firstName;
    private string lastName;

    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }

    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
}

In the bad example, FirstName and LastName are defined as public member variables, which means that any code can access and modify them directly. This can lead to unexpected changes and make it harder to maintain the code.

In the good example, firstName and lastName are defined as private member variables, and their values can only be accessed or modified through public properties with getter and setter methods. This ensures that the values can only be modified in a controlled way and allows for additional logic or validation to be added to the properties if needed.

What is the difference, Christian? They can still be modified?

Yeah 😅 in both examples, the FirstName and LastName properties can still be modified. However, in the bad example, they are defined as public member variables, which means that any code can access and modify them directly. This can lead to unexpected changes and make it harder to maintain the code.

In the good example, the firstName and lastName member variables are defined as private, which means that they can only be accessed or modified within the Person class itself. The FirstName and LastName properties are then defined with a public getter and setter method, which allows external code to access and modify the values in a controlled way. By using properties instead of public member variables, you can add additional logic or validation to the setter methods if needed, which can help prevent unexpected changes and make the code more maintainable. That's why you should do it this way.

Use the readonly modifier for constant member variables

If you have member variables that should not be modified after initialization, use the readonly modifier to prevent accidental changes.

// Good:
public class Circle
{
    private readonly double pi = 3.14;
    private readonly double radius;
}

// Bad:
public class Circle
{
    private const double pi = 3.14;
    private double radius;
}

Enums

Use PascalCase for enum type names

Using PascalCase for enum type names is a recommended best practice in C# coding standards. PascalCase is a naming convention that capitalizes the first letter of each word in a name, with no spaces or underscores in between.

Using PascalCase for enum type names can help make the code more readable and consistent, as it provides a clear and consistent way to name and identify the enum type. It also makes the enum type name stand out from other variables and types in the code.

Here's an example of using PascalCase for an enum type name:

public enum PaymentMethod
{
    CreditCard,
    DebitCard,
    PayPal,
    Bitcoin
}

In the example above, we use PascalCase for the enum type name "PaymentMethod". This makes it easy to identify that this is an enum type and what it represents. Additionally, each enum value is also named using PascalCase, which helps make the code more readable and consistent.

Use meaningful names for enum values

Using meaningful names for enum values is an important best practice in C# coding standards. Enum values should be named in a way that accurately represents their purpose or meaning in the code. This helps to make the code more readable and self-explanatory, which can improve maintainability and reduce the likelihood of errors.

For example, consider the following enum type:

public enum UserRole
{
    Admin,
    Manager,
    Employee
}

In the code example above, the enum values are named in a way that clearly represents the roles they represent. This makes it easy for other developers to understand the purpose of the enum type and its values, without needing to read through additional documentation.

It's important to note that the names of enum values should be meaningful and self-explanatory, but also concise and clear. Avoid using long or overly complicated names, as this can make the code more difficult to read and understand.

Use the "singular" form for enum type names

When defining enum types in C#, it is generally recommended to use the singular form for the type name. This is because an enum type represents a single value from a set of possible values, rather than a collection or group of values.

For example, consider an enum type that represents different types of animals:

public enum AnimalType
{
    Dog,
    Cat,
    Bird,
    Fish
}

In the code example above, the enum type is named AnimalType using the singular form, which accurately reflects the fact that each value represents a single type of animal.

Using the singular form for enum type names can help to improve the clarity and readability of your code. It also follows a consistent naming convention, making your code easier to understand for other developers who may be working on the same project.

However, it's important to note that there may be cases where using the plural form for enum type names may make more sense, such as when the enum represents a collection or group of related values. In these cases, it's important to use your best judgment and choose a name that accurately reflects the purpose and meaning of the enum type.

Examples of good and bad enum naming practices

Naming is an essential part of writing clean and maintainable code. The same applies to enum types, where a well-thought-out naming convention can make code easier to read, understand and maintain. In this section, we will discuss some examples of good and bad enum naming practices.

Good practices for enum naming include using meaningful names for enum values, following a consistent naming convention, and avoiding ambiguous or confusing names. Consider the following example:

// Good example: using meaningful and clear names 
// for enum values
public enum Gender
{
    Male,
    Female,
    NonBinary
}

In the code example above, the enum type is named Gender and uses meaningful and clear names for each value. The use of PascalCase and singular form for the enum type name follows the C# naming convention, making it easy to read and understand.

On the other hand, poor naming practices for enum types may include using abbreviated or cryptic names, using non-descriptive or unclear names, and using inconsistent naming conventions. For example:

// Bad example: using abbreviated and unclear names 
// for enum values
public enum Colors
{
    R,
    G,
    B
}

In this example, the enum type is named Colors, but the values are named with abbreviated letters that are not immediately clear to the reader. Additionally, the name of the enum type is plural, which does not accurately reflect the singular nature of each value.

Enum Types and Suffixes

When working with enums in C#, there are some best practices to follow when defining and using them. One such practice is to define a custom type for the enum, instead of using the built-in System.Enum type. This allows you to define additional functionality and behavior for the enum type.

Here is an example of defining an enum with a custom type:

public enum Size
{
    Small,
    Medium,
    Large
}

public class Pizza
{
    public Size Size { get; set; }
    // other pizza properties and methods
}

In this example, we have defined an enum called Size and used it as a property in a Pizza class. Notice that the enum type is not defined using the System.Enum type, but rather as a custom type. This allows us to add additional functionality to the Size enum type, such as methods or properties specific to pizza sizes.

Another best practice when working with enums is to use a suffix in the enum value names to provide additional context. For example, if you have an enum for different types of fruit, you might use the suffix Fruit in each value name:

public enum FruitType
{
    AppleFruit,
    BananaFruit,
    OrangeFruit
}

This provides additional context to the value names and helps prevent confusion if there are other types of objects or concepts with similar names.

It's important to note that while using a suffix can be helpful, it should not be overused or relied on too heavily. The suffix should only be used when it provides meaningful information and context to the enum value names.

Summary

Following coding standards is important for maintaining code consistency and readability. It ensures that code is easy to understand and maintain by anyone who reads it. In this blog post, I covered some best practices for C# coding standards, including class names, variable names, identifiers, constants, abbreviations, no underscores, type names, interfaces, file names, namespaces, curly brackets, member variables, enums, enum types, and enum suffixes.

By adhering to these best practices, we can create code that is more readable, maintainable, and efficient. It also helps to ensure that the code is consistent across the entire project and that new developers can easily pick up the codebase.

It is important to note that these best practices are not exhaustive and that there may be additional considerations for specific projects or teams. However, following these guidelines is a good starting point for ensuring that your code is well-written and easy to maintain.

By writing clean and consistent code, we can reduce the time it takes to debug, maintain, and improve the codebase. In turn, this can lead to more efficient development and better software overall. So, take some time to review your code and implement these best practices where possible. Your future self (and your teammates) will thank you! Happy coding! ✌️

Christian Schou
My name is Christian. I am a 27-year-old Software & DevOps Engineer with a passion for .NET, Cloud, and Containers. In my spare time, I share my knowledge and love teaching other people about tech.
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Tech with Christian.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.