C# Code Writing Suggestions
C# Code Writing Suggestions
AutoMapper
When you create a CreateMap function for automapper, you must follow
the following template:
CreateMap<User, UserDetails>()
.ForMember(
dest => dest.Id,
option => option.MapFrom(user => user.Id))
.ForMember(
dest => dest.GivenName,
option => option.MapFrom(user => user.GivenName))
.ForMember(
dest => dest.Surname,
option => option.MapFrom(user => user.Surname))
.ForMember(
dest => dest.JobTitle,
option => option.MapFrom(user => user.JobTitle))
.ForMember(
dest => dest.OfficeLocation,
option => option.MapFrom(user => user.OfficeLocation))
.ForMember(
dest => dest.DisplayName,
option => option.MapFrom(user => user.DisplayName))
.ForMember(
dest => dest.Department,
option => option.MapFrom(user => user.Department))
.ForMember(
dest => dest.EmployeeId,
option => option.MapFrom(user => user.EmployeeId))
.ForMember(
dest => dest.SamAccountName,
option => option.MapFrom(user =>
user.OnPremisesSamAccountName))
.ForMember(
dest => dest.UserPrincipalName,
option => option.MapFrom(user => user.UserPrincipalName))
.ForMember(
dest => dest.Mail,
option => option.MapFrom(user => user.Mail))
.ForMember(
dest => dest.BusinessPhones,
option => option.MapFrom(user => user.BusinessPhones))
.ForMember(
dest => dest.MobilePhone,
option => option.MapFrom(user => user.MobilePhone));
About return
When you use return it's necessary to have a new line before, exception
is when the return is the first statement for that
// Correct
static double CalculateMaxAreas(int r1, int r2)
{
double area1 = r1 * r1 * Math.PI;
double area2 = r2 * r2 * Math.PI;
double maxArea = area1 > area2 ? area1 : area2;
return maxArea;
}
// Avoid
static double CalculateMaxAreas(int r1, int r2)
{
double area1 = r1 * r1 * Math.PI;
double area2 = r2 * r2 * Math.PI;
double maxArea = area1 > area2 ? area1 : area2;
return maxArea;
}
// Exceptions
static double CalculateArea(int r)
{
return r * r * Math.PI;
}
Use directly return when is only one statement
// Correct
static double CalculateArea(int r)
{
return r * r * Math.PI;
}
// Avoid
static double CalculateArea(int r)
{
double area = r * r * Math.PI;
return area;
}
About await
When using wait it is mandatory to use .ConfigureAwait (false)
// Correct
var users = await userManager.GetAllAsync().ConfigureAwait(false);
// Avoid
var users = await userManager.GetAllAsync();
String interpolation
The $ special character identifies a string literal as an interpolated
string. An interpolated string is a string literal that might contain interpolation
expressions. When an interpolated string is resolved to a result string, items with
interpolation expressions are replaced by the string representations of the
expression results. This feature is available starting with C# 6.
string name = "Mark";
var date = DateTime.Now;
// Composite formatting:
Console.WriteLine("Hello, {0}! Today is {1}, it's {2:HH:mm} now.",
name, date.DayOfWeek, date);
// String interpolation:
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's
{date:HH:mm} now.");
// Both calls produce the same output that is similar to:
// Hello, Mark! Today is Wednesday, it's 19:40 now.
LINQ queries
Avoid using abbreviations such as letters in LINQ queries
// Correct
return await db.ServiceUsers
.OrderBy(user => user.Forename)
.ToDictionaryAsync(key => key.Id, value => value.DisplayName)
.ConfigureAwait(false);
// Avoid
return await db.ServiceUsers
.OrderBy(a => a.Forename)
.ToDictionaryAsync(k => k.Id, v => v.DisplayName)
.ConfigureAwait(false);
Order items
Use proper order of items
Within a class, struct, or interface order by group:
Constant Fields
Fields
Constructors
Finalizers (Destructors)
Delegates
Events
Enums
Interfaces (Interface implementations)
Properties
Indexers
Methods
Structs
Classes
Withing each group order by access:
public
internal
protected internal
protected
private protected
private
Within each access groups, order by static, then non-static:
static
non-static
Then, order by readonly, then non-readonly
readonly
non-readonly
class Application
{
public const int const1 = 1;
internal const int const2 = 2;
protected internal const int const3 = 3;
protected const int const4 = 4;
private protected const int const5 = 5;
private const int const6 = 6;
public Application(...)
internal Application(...)
protected internal Application(...)
protected Application(...)
private protected Application(...)
private Application(...)
// Avoid
var x = Method(
100,
200,
300);
var y = Method(
100,
200,
300);
Break correctly
Break:
. - before dot separator
await ExtrapolateTheWorld().ToList();
, - after comma
Task<bool> ExtrapolateTheWorld(int id, bool active = true, string
flag);
=> - after lambda operator
return await db.Towers
.Include(glass => glass.Details)
.Where(tower => tower.Active == true)
.OrderBy(tower => tower.Height)
.AsNoTracking()
.ToListAsync()
.ConfigureAwait(false);
{ - before opening curly brackets for in-line initializations
int[] ints = new[] { 1, 2, 3, 4, 5900019,8873 };
before logical operators
int xantalya = (a + b) * c;
int yamila = a / (b - c);
Split method arguments by the following rule: all on same line, or all
on own line.
// Correct
Method(arg1, arg2, arg3);
await Method(
arg1, arg2, arg3);
var x = Method(
arg1,
arg2);
if ((expression1 || expression2)
&& (expresion3 && expression4)
|| expression5
|| expression6)
var x = collection
.Where(item => item.RegisterDate.Year > 2000)
.Join(
collection2,
collection1 => new { Date = collection1.RegisterDate, Owner
= collection1.RegisteredBy },
collection2 => new { Date = collection2.Date, Owner =
collection2.User },
(collection1, collection2) => new { OldEntry = collection1,
NewEntry = collection2 })
.Select(pair =>
new
{
Id = pair.OldEntry.Id,
X = pair.OldEntry.X,
Y = pair.NewEntry.Y
})
.ToList();
Naming convention
Naming
Use PascalCasing to name classes, methods, and properties
class ClassName
void MethodName();
string PropertyName { get; set; }
Use camelCasing to name method arguments, local variables, and
fields
string fieldName;
void Method(int argumentOne, int argumentTwo);
Method(argumentOne, argumentTwo);
int localName = 10;
Do not use Hungarian notation or any other type identification in
identifiers
// Correct
int counter;
string name;
// Avoid
int iCounter;
string strName;
Do not use Screaming Caps for constants or readonly variables
// Correct
public static const string ShippingType = "DropShip";
// Avoid
public static const string SHIPPINGTYPE = "DropShip";
Avoid using Abbreviations. Exceptions: abbreviations commonly
used as names, such as Id, Xml, Ftp, Uri
// Correct
UserGroup userGroup;
Assignment employeeAssignment;
// Avoid
UserGroup usrGrp;
Assignment empAssignment;
// Exceptions
CustomerId customerId;
XmlDocument xmlDocument;
FtpHelper ftpHelper;
UriPart uriPart;
Use PascalCasing for abbreviations 3 characters or more (2 chars
are both uppercase)
HtmlHelper htmlHelper;
FtpTransfer ftpTransfer;
UIControl uiControl;
Do not use Underscores in identifiers. Exception: you can prefix
private static variables
// Correct
public DateTime clientAppointment;
public TimeSpan timeLeft;
// Avoid
public DateTime client_Appointment;
public TimeSpan time_Left;
// Exception
private DateTime _registrationDate;
Namespaces
Use PascalCasing to name namespaces.
namespace Company.Product.Module.SubModule
namespace Product.Module.Component
namespace Product.Layer.Module.Group
Separate non-static from static usings
using System;
using System.Linq;
using static System.Linq.Enumerable;
using static System.Math;
Interfaces
Prefix interface names with the letter I
interface names are nouns or adjectives.
interface ILogger
interface IApplicationBuilder
interface ICollection
Do not (habitually) add new signatures at the end of the
interface
Classes
Use PascalCasing when abbreviating three or more characters
When there are two characters, both are uppercase.
HtmlHelper htmlHelper;
XmlDocument xmlDocument;
IOException ioException;
UIElement uiElement;
use noun or noun phrases to name a class.
public class Employee
{
}
public class BusinessLocation
{
}
public class DocumentCollection
{
}
Type names
use predefined type names instead of system type names like
Int16, Single, UInt64
will make code more natural to read
// Correct
string firstName;
int lastIndex;
bool isSaved;
// Avoid
String firstName;
Int32 lastIndex;
Boolean isSaved;
Enums
use singular names for enums. Exception: bit field enums
[Flags] tag it's used to hold multiple values (using bitwise
'OR')
// Correct
public enum Color
{
Red,
Green,
Blue,
Yellow,
Magenta,
Cyan
}
// Exception
[Flags]
public enum Dockings
{
None = 0,
Top = 1,
Right = 2,
Bottom = 4,
Left = 8
}
Enums Types
Do not explicitly specify a type of an enum or values of enums
(except bit fields) because can create confusion when relying on actual types and
values
// Don't
public enum Direction : long
{
North = 1,
East = 2,
South = 3,
West = 4
}
// Correct
public enum Direction
{
North,
East,
South,
West
}
Enums Suffix
Do not suffix enum names with Enum
// Don't
public enum CoinEnum
{
Penny,
Nickel,
Dime,
Quarter,
Dollar
}
// Correct
public enum Coin
{
Penny,
Nickel,
Dime,
Quarter,
Dollar
}
Member variables
declare all member variables at the top of a class, with static
variables at the very top
generally, this practice prevents the need to hunt for variable
declarations
// Correct
public class Account
{
public static string BankName;
public static decimal Reserves;
// Constructor
public Account()
{
// ...
}
}