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.
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();
}
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
Use namespace to group related classes
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! ✌️