Remember that at the end of the day these are only recommendations.
Tool configuration links only provides the reference to the style for a given tool which doesn't mean that this style guide uses the same recommendation by the tool.
We include a ruleset file for StyleCop where we try to keep the styles matching.
For this to work you need to add the NuGet package and manually add the ruleset file to each project.
<CodeAnalysisRuleSet>..\CSharpStyleguide.ruleset</CodeAnalysisRuleSet>
Another thing to consider is that auto-generated code (like EF7 migrations) should not be required to comply with these rules. In order to bypass the ruleset, you need to manually add at the top of the file:
// <auto-generated/>
If you are used to .editorconfig files then you can grab the one we include here.
Tool configuration: EditorConfig
Exceptions are allowed. For more information read this fantastic post from Eric Lippert.
// Bad
HttpClient httpClient = new HttpClient();
// Good
var httpClient = new HttpClient();
// Allowed for using an interface instead of a class
IUserService userService = new UserService();Tool configuration: SA1101 EditorConfig
It is redundant and does not add for readability. For more information read this SO post.
// Bad
this.ValidateParameters();
// Good
ValidateParameters();Tool configuration: SA1121 EditorConfig
// Bad
String.IsNullOrEmpty(name);
// Good
string.IsNullOrEmpty(name);// Bad
var message = string.Format("My name is {0} {1}.", person.FirstName, person.LastName);
// Good
var message = $"My name is {person.FirstName} {person.LastName}.";Tool configuration: EditorConfig
You can enable the "View White Space" option and use CTRL+R/CTRL+W keyboard shortcuts.
// Bad
public void SomeMethod()
{
∙∙DoSomethingFirst();
∙∙DoSomethingLater();
}
// Good
public void SomeMethod()
{
∙∙∙∙DoSomethingFirst();
∙∙∙∙DoSomethingLater();
}Tool configuration: SA1507
// Bad
public void SomeMethod()
{
DoSomethingFirst();
DoSomethingLater();
DoSomethingAtTheEnd();
}
// Good
public void SomeMethod()
{
DoSomethingFirst();
DoSomethingLater();
DoSomethingAtTheEnd();
}Tool configuration: EditorConfig
// Good
public string Name { get; private set; }
// Good
public string UppercaseName => Name.toUpperCase(); Tool configuration: SA1500
// Bad
public void SomeMethod() {
// ...
}
// Good
public void SomeMethod()
{
// ...
}Tool configuration: SA1508 SA1509
// Bad
public void SomeMethod()
{
DoSomethingFirst();
DoSomethingLater();
}
// Good
public void SomeMethod()
{
DoSomethingFirst();
DoSomethingLater();
}Tool configuration: SA1503
// Bad
if (payment == null)
{
return "A payment is required.";
}
// Good
if (payment == null)
return "A payment is required.";Tool configuration: SA1001
A comma should be followed by a single space and never be preceded by any whitespace.
// Bad
var result = Calculate(3 , 5);
var result = Calculate(3,5);
// Good
var result = Calculate(3, 5);Tool configuration: SA1003
An operator symbol must be surrounded by a single space on either side.
// Bad
var result = 5+3;
var result = 5 +3;
var result = 5+ 3;
// Good
var result = 5 + 3;Except for unary operators:
// Bad
var result = ! toggle;
// Good
var result = !toggle;Tool configuration: SA1008
// Bad
var result = Calculate( 3, 5);
// Good
var result = Calculate(3, 5);Tool configuration: SA1009
// Bad
var result = Calculate(3, 5 );
// Good
var result = Calculate(3, 5);Tool configuration: SA1010
// Bad
var element = myArray [index];
var element = myArray[ index];
// Good
var element = myArray[index];Tool configuration: SA1011
// Bad
var element = myArray[index ];
var element = myArray[index] ;
// Good
var element = myArray[index];Tool configuration: SA1012
// Bad
var names = new string[] {"Marie", "John", "Paul" };
// Good
var names = new string[] { "Marie", "John", "Paul" };Tool configuration: SA1013
// Bad
var names = new string[] { "Marie", "John", "Paul"};
// Good
var names = new string[] { "Marie", "John", "Paul" };Tool configuration: SA1201
- Constants
- Fields
- Constructors
- Delegates
- Events
- Properties
- Methods
- Finalizers (Destructors)
Tool configuration: SA1202
publicinternalprotected internalprotectedprivate
Tool configuration: SA1206
- Access modifiers
static- All other keywords
// Bad
override public int Area()
{
// ...
}
// Good
public override int Area()
{
// ...
}// Bad
static public void Drive() { }
// Good
public static void Drive() { }Tool configuration: SA1210
// Bad
using System;
using Newtonsoft.Json;
using System.Linq;
using System.Collections.Generic;
// Good
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;// Bad
var hc = new HttpClient();
// Good
var httpClient = new HttpClient();// Bad
var e = "test@test.com";
// Good
var email = "test@test.com";Use PascalCase for namespaces, classes, enums, structs, constants, delegates, events, methods and properties
Tool configuration: SA1300 SA1303
// Bad
public class file_reader
{
// ...
}
// Good
public class FileReader
{
// ...
}// Bad
public const int TIME_IN_SECONDS = 5;
// Good
public const int TimeInSeconds = 5;// Bad
public string secondAddress { get; set; }
// Good
public string SecondAddress { get; set; }// Bad
var FileReader = new FileReader();
var file_reader = new FileReader();
var _fileReader = new FileReader();
// Good
var fileReader = new FileReader();Tool configuration: SA1309
// Bad
private IUserService UserService;
private IUserService userService;
private IUserService user_service;
// Good
private IUserService _userService;Tool configuration: SA1302
// Bad
public interface LoggerInterface
{
// ...
}
// Good
public interface ILogger
{
// ...
}// Bad
public async Task<int> GetLatestPosition()
{
// ...
}
// Good
public async Task<int> GetLatestPositionAsync()
{
// ...
}We all know testing code is not built the same way that regular code is, because its purpose is different. This is why we have special guidelines for this type of code.
// Bad
namespace MyProject
{
[TestClass]
public class MyService
{ }
}
// Good
namespace MyProject
{
[TestClass]
public class MyServiceTests
{ }
}Use the same folder structure / namespace structure as the code that's being tested. This makes it easier to find test classes for a particular class and classes being tested by a particular test class.
The name needs to express:
- What is being tested
- What should happen.
- Under which conditions.
What is being tested will usually be a particular method. (These are unit tests, after all.) For integration tests, there is more freedom in choosing this portion, but should still be a good name indicating the scenario being tested.
What should happen should be concise and to the point. Avoid should or must or expressions of rule/desire. The test itself is that validation, there's no need to specify it.
The conditions can be avoided if "normal" conditions are easy to assume and they're the one being tested. For example, a "must work under regular conditions" test says nothing. A good name would be "SaveToDatabase_PersistsChanges"
// Bad: Uses filler words
public void SaveToDatabase_ShouldPersistChanges() { }
// Bad: does not correctly indicate responsibility
public void SaveToDatabase_Works() { }
// Good
public void SaveToDatabase_PersistsChanges() { }
// Also good
public void SaveToDatabase_ThrowsArgumentException_WhenConnectionStringIsNotPresent() { }We generally avoid using #region since it is a symptom of poorly designed code. However, tests classes are likely to grow if the same class handles several scenarios to be tested, or if several methods are exposed. Group them by the method being tested.
Include overloaded versions of methods in the same region.
#region SaveToDatabase
public void SaveToDatabase_PersistsChanges() { }
public void SaveToDatabase_ThrowsArgumentException_WhenConnectionStringIsNotPresent() { }
#endregion
#region RetrieveFromDatabase
public void RetrieveFromDatabase_RetrievesClientById() { }
public void RetrieveFromDatabase_RetrievesClientsByName() { }
public void RetrieveFromDatabase_ThrowsArgumentOutOfRangeException_WhenIdIsEmptyGuid() { }
#endregionThank you to all who contributed to any of the styles in this document or mentioned references.