Skip to main content

C# Code Standards

Introduction

This document describes the coding standards for C# code in the City Skylines project.

Naming Conventions

General Naming Conventions

  • Pascal Case: Use Pascal case for all public member, type, and namespace names consisting of multiple words. Pascal case is identical to camel case, but the first letter of the identifier is capitalized. For example, BackColor.
  • Camel Case: Use camel case for parameter names and local variables. Camel case is identical to Pascal case, but the first letter of the identifier is lowercase. For example, backColor.
  • Abbreviations: Do not use abbreviations in identifiers. For example, use GetWindow instead of GetWin.
  • Acronyms: Acronyms should be treated as words in identifiers. For example, use XmlHttpRequest instead of XMLHTTPRequest.
  • Underscores: Do not use underscores in identifiers. For example, use backColor instead of back_Color.

Namespace Naming Conventions

  • Namespace Naming: Use a single, top-level namespace for the project. The namespace should be the name of the project. For example, the namespace for the City Skylines project is CitySkylines.

Type Naming Conventions

  • Class Naming: Use nouns or noun phrases to name classes. Class names should be clear and descriptive. For example, use Customer instead of Data.
  • Interface Naming: Use a noun or noun phrase to name interfaces. Interface names should be clear and descriptive. For example, use ILogger instead of ILog.
  • Enum Naming: Use a singular noun for enum types. Enum names should be clear and descriptive. For example, use DayOfWeek instead of Days.

Member Naming Conventions

  • Property Naming: Use nouns or noun phrases to name properties. Property names should be clear and descriptive. For example, use FirstName instead of Name.
  • Method Naming: Use verbs or verb phrases to name methods. Method names should be clear and descriptive. For example, use CalculateTotal instead of Calculate.
  • Event Naming: Use a verb or verb phrase to name events. Event names should be clear and descriptive. For example, use Click instead of OnClick.

Parameter Naming Conventions

  • Parameter Naming: Use camel case for parameter names. Parameter names should be clear and descriptive. For example, use customerName instead of name.

Bracing

  • Brace Placement: Place the opening brace on the same line as the statement it belongs to. Place the closing brace on a new line by itself. For example:

    if (condition)
    {
    // Code here
    }

Indentation

  • Indentation: Use four spaces for indentation. Do not use tabs for indentation.

Comments

  • Comments: Use comments to explain the intent of the code. Comments should be clear and concise. Use comments to explain complex algorithms, business rules, or design decisions. Comments should be written in English.
// Calculate the total price of the items in the cart
public decimal CalculateTotal() {
decimal total = 0;

foreach (var item in cartItems) {
total += item.Price;
}

return total;
}

Documentation Comments

  • Documentation Comments: Use XML documentation comments to document types and members. Documentation comments should describe the purpose of the type or member, its parameters, return value, and exceptions that can be thrown. Documentation comments should be written in English.
/// <summary>
/// Represents a customer in the system.
/// </summary>
public class Customer {
/// <summary>
/// Gets or sets the customer's first name.
/// </summary>
public string FirstName { get; set; }

/// <summary>
/// Gets or sets the customer's last name.
/// </summary>
public string LastName { get; set; }

/// <summary>
/// Gets the customer's full name.
/// </summary>
public string FullName {
get {
return $"{FirstName} {LastName}";
}
}
}

Error Handling

  • Error Handling: Use exceptions to handle errors and unexpected conditions. Do not use exceptions for normal control flow. Use specific exception types to indicate the type of error that occurred. Catch exceptions at the appropriate level and handle them appropriately.
try {
// Code that may throw an exception
} catch (Exception ex) {
// Handle the exception
}

throw new InvalidOperationException("Invalid operation");
  • Exception Handling: Use specific exception types to indicate the type of error that occurred. Use try-catch blocks to handle exceptions. Use throw statements to rethrow exceptions.
try {
// Code that may throw an exception
} catch (ArgumentNullException ex) {
// Handle ArgumentNullException
} catch (InvalidOperationException ex) {
// Handle InvalidOperationException
} catch (Exception ex) {
// Handle other exceptions
throw;
}
  • Custom Exceptions: Use custom exception types to represent specific errors in the application. Create custom exception classes that inherit from the Exception class.
public class CustomException : Exception {
public CustomException(string message) : base(message) {
}
}
  • Logging Exceptions: Use a logging framework to log exceptions. Log exceptions at the appropriate level and include relevant information such as the exception message, stack trace, and inner exceptions.
try {
// Code that may throw an exception
} catch (Exception ex) {
logger.Error(ex, "An error occurred");
}

Variables

  • Variable Declaration: Declare variables close to where they are used. Use meaningful variable names that describe the purpose of the variable. Use the var keyword for local variables when the type is obvious from the initialization expression.
var firstName = "John";
var lastName = "Doe";

Constants

  • Constants: Use const for values that do not change. Use readonly for values that are initialized at runtime. Use uppercase letters and underscores for constant names.
public const int MaxItems = 10;
public readonly string ConnectionString = "Server=localhost;Database=Northwind;User=sa;Password=pass";

Properties

  • Auto-Implemented Properties: Use auto-implemented properties for simple properties that do not require additional logic in the getter or setter.
public string FirstName { get; set; }
  • Full Properties: Use full properties for properties that require additional logic in the getter or setter.
private string firstName;

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

Loops

  • For Loop: Use a for loop when the number of iterations is known. Use a for loop to iterate over a range of values.
for (int i = 0; i < 10; i++) {
// Code here
}
  • Foreach Loop: Use a foreach loop to iterate over a collection. Use a foreach loop when the number of iterations is not known.
foreach (var item in items) {
// Code here
}
  • While Loop: Use a while loop when the number of iterations is not known. Use a while loop to iterate until a condition is met.
while (condition) {
// Code here
}

Switch Statements

  • Switch Statement: Use a switch statement to select one of several code blocks to execute. Use a switch statement when there are multiple cases to handle.
switch (day) {
case DayOfWeek.Monday:
// Code here
break;
case DayOfWeek.Tuesday:
// Code here
break;
default:
// Code here
break;
}

Using Statements

  • Using Statements: Use using statements to ensure that resources are properly disposed of. Use using statements for types that implement the IDisposable interface. Do not use using statements for types that do not implement the IDisposable interface.
using (var stream = new FileStream("file.txt", FileMode.Open)) {
// Code here
}

Using Directives

  • Using Directives: Use using directives to import namespaces at the top of the file. Use separate using directives for each namespace. Do not use using directives for namespaces that are not used in the file.
using System;
using System.Collections.Generic;
using System.Linq;

Code Formatting

  • Code Formatting: Use a consistent code formatting style throughout the project. Use spaces around operators, after commas, and after colons. Use a single space between keywords and opening parentheses. Use a single space between the // characters and the comment text.
if (condition) {
// Code here
} else {
// Code here
}

Null Checking

  • Null Checking: Use null checking to handle null values. Check for null values before accessing properties or methods on an object. Use the null-conditional operator (?.) to safely access properties or methods on an object that may be null.
if (customer != null) {
var fullName = customer.FullName;
}

var fullName = customer?.FullName;

References