10 C# conventions you should know
Besides learning the syntax of a programming language, it’s just as important to know its conventions. Conventions consist of commonly accepted things such as how you spell variable names, where you place brackets, etc.
Violating the naming conventions won’t prevent your code from compiling and running, but it will make the code less readable and maintainable. It will also make the programmer perceived as inexperienced with the language.
In today’s article, we will look at the conventions used by C# developers that are commonly violated. If you want to know how to use the C# language in the way experienced developers use it, you absolutely must know these conventions.
We will not discuss any new conventions enabled by the recent language features, such as using null equality vs null pattern matching. Likewise, we will not discuss the conventions that people argue about, such as whether or not to use the var
keyword. We will focus purely on the fundamental and universally accepted conventions that apply to any language version.
1. Naming classes
In C#, classes are named in PascalCase. This is when each new word in the name starts with the capital letter. The remaining letters of the word are lowercase. For example, a class that updates user information may be called UserUpdater
.
Acronyms are considered to be individual words and the same rules apply to them. For example, a class that represents an HTTP client would be called HttpClient
.
2. Naming interfaces
The same principle applies to interfaces. However, interface names also have the “I” prefix. For example, an interface that defines the contract for the transactions repository would be called ITransactionsRepository
.
I know what some of you may say. Using the “I” prefix in the interface names is not what Uncle Bob said in his Clean Code book.
Well, .NET developers disagree. And this is now a standard C# convention whether everyone likes it or not.
I would say it’s a good convention. Unlike in some other languages (especially Java and Kotlin), this convention makes it easy to identify whether a type is a class or an interface without having to look up its definition.
3. Naming enum values
In some languages, enum values are named by using all uppercase letters with underscores between the words. This convention fairly commonly creeps into C# code.
However, this is not a C# convention. In C#, we would use PascalCase for enum value names, just like we do for class names.
4. Naming methods
Many popular languages use camelCase conventions for naming methods and functions. However, this is not what C# developers do. In C#, all methods are named by using PascalCase.
Of course, there is nothing that will stop you from using Java-style camelCase in C# method names. Your code will still compile. But you immediately will stand out as someone who doesn’t have much experience with C#.
5. Naming fields
The conventions for naming fields in C# are more nuanced. Typically, the following rules will be applied:
If this is a class-wide private or protected field, we would use camelCase with the underscore prefix, e.g.
_usersApiClient
.If it’s a local variable, we would use camelCase, e.g.
userApiClient
.
This is, pretty much, it. However, you may ask: “What about the public fields?”. Well, those are irrelevant because of the next point.
6. Fields vs properties
In C#, fields and properties serve a very similar purpose. However, they are different concepts.
Fields are class-wide variables that store data. Properties are also class-wide variables that store data, but they are more complex.
Properties have the so-called “getters” and “setters”. The job of getters is to retrieve the data while the job of setters is to store the data. Getters and setters can have custom logic, so you can do more than just store and read data.
So, since fields and properties are so similar, how do we decide what to use? Well, here are some simple rules:
Any private data should be represented by fields.
Any public data should be represented by properties.
This is because, with custom getters and setters, properties can encapsulate private data. If the data is already private, there is nothing to encapsulate.
Of course, there is nothing that would stop you from having a public field or private property, but this would violate the commonly accepted convention.
Regarding the naming conventions, property names should use PascalCase, as almost anything else in C#.
7. The usage of curly brackets
Not all languages use curly brackets to define the boundaries of code blocks, such as methods and functions. But those that do, often have the opening bracket on the same line as the method definition, like so:
int someMethod() {
// some logic
}
In C#, however, the opening bracket goes onto the next line. Therefore, a C# equivalent of the above method would look like this:
public int SomeMethod()
{
// some logic
}
This applies to everything else that uses curly brackets, such as loops, if conditions, switch statements, etc.
8. Namespace references
In C#, if you need to reference a type that resides in a particular namespace (e.g. referencing types from an external library), you would need to reference this namespace at the beginning of your file via a using
statement. Here is an example:
using Newtonsoft.Json;
These statements have their own conventions, which are as follows:
We would only reference the namespaces that we need
The
using
statements will be sorted in the alphabetical orderThe exception is the namespaces with the
System
prefix, which would be referenced first before any other namespaces
9. If-else blocks
In C#, if the logic inside an if condition consists of multiple lines, you would wrap it in curly brackets. However, if it’s a single-line expression, it’s up to you whether or not to use curly brackets. It’s absolutely fine to do this:
if (amountIsSufficient)
paymentService.ProcessTransaction();
However, what you will never do is mix and match these conventions with the same if-else block. You would never do this:
if (amountIsSufficient)
paymentService.ProcessTransaction();
else
{
string message = "Could not process a payment due to insufficient funds";
logger.LogWarning(message);
notificationService.NotifyUser(user.Id, message);
}
You would do this instead:
if (amountIsSufficient)
{
paymentService.ProcessTransaction();
}
else
{
string message = "Could not process a payment due to insufficient funds";
logger.LogWarning(message);
notificationService.NotifyUser(user.Id, message);
}
10. Setting dependencies inside a class
In any sufficiently complex application, your classes would depend on other classes. Very often, such dependencies would be managed via the class constructor. Here are some rules for managing such dependencies in C#:
The dependency would be stored in a private readonly field so it’s hidden from the outside world and cannot accidentally be replaced for the duration of the lifespan of the class
If possible, the type of this field would be an interface rather than a concrete implementation, so it can be easily substituted for another implementation under different scenarios or for testing
This is how these principles are implemented in practice:
public class UserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
}
Wrapping up
This article presented the most fundamental conventions; however, there are many more conventions used by C#. If you want to get a more detailed understanding of them, you can follow this official guide from Microsoft:
https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines
Until next time!