Coding Style C# Coding Style C#

These guidelines are important for all employees (current and future) at Delta Engine to write clean TDD C# code and they are of course used for the Delta Engine and internal projects as well. All our guidelines are based on the Clean Code book, which is required reading for everyone. You can also watch the great Clean Coders Videos by Uncle Bob to learn about TDD in a fun way.

ReSharper issues and NDepend rules enforced by the TeamCity CI builds on every check-in make sure these rules are followed by every person with every commit. With ReSharper to make sure you have always zero code issues in each file (green icon on top of the scrollbar, or if you have a fast computer use solution wide analysis all the time). For NDepend it is much harder to check all the code at once, you can install it locally and check out the rules, but what works best in general is to keep code clean at all times and quickly fix any violations before they pile up and get harder and harder to fix. Always leave code in a better state than you found it! Practice TDD (Test-driven development), Clean Code and do katas (we have kata wednesday every week).

Please read the rules here carefully, all code and your tasks will only be accepted into the default/master branch if:
  • It compiles and works (feature complete, never check-in code that does not compile or does not work)
  • All unit tests and integration tests pass (visual and functional tests will be tested automatically by our TeamCity CI (continous integration) server)
  • Covers all lines of code with tests (see NCrunch, must be 100%, exclude external libraries, UI and any kind of untestable app code)
  • Passes ReSharper issues (DeltaEngine.Global.sln.DotSettings) analysis without any errors or warnings (you can see these in Visual Studio with ReSharper the same way)
  • Passes all NDepend rules, which go a bit above the ReSharper issues and are more global (class length, method complexity, naming, etc.)
  • Does not cause any part of the engine or tests to run slowly (e.g. less than 60fps, unit tests taking longer than 100ms or if all tests take >5s) This is by far the hardest part, especially for advanced code and when using external libraries!
Our rules are a bit more strict, but passing all of these gets your CI green and we still do Code Review via a fellow employee if it all fits well with the other code and makes sense, this process is mostly to make code reviews much easier and more enjoyable, we don't have to repeat the same low level rules on every code review as code must pass all of the above to get into the system anyway and before any code review should be submitted!

  • Visual Studio 2022 and .NET 6 is used for development, we also use Visual Studio Code for JavaScript, C++, Python and on Linux a lot. New projects should usually just be class libraries or NUnit projects, rarely console apps or windows apps or web services for the end product. Always use the tools described above and follow the TeamCity CI process (e.g. DeltaEngine.sln.DotSettings for Resharper).
  • Internally we use kiln, which supports git and hg. Internal code is always done in hg via TortoiseHg, everything public we push on github.
    • Internally we use the Kiln Client, which is based on TortoiseHG and makes some useful additions. Please make sure you install this instead of TortoiseHG (delete mercurial.ini if you have used TortoiseHG before) because this will setup your username and all needed extensions automatically for you. Set "Push after commit" and "Update after pull" and you are done.
    • After TortoiseHg is installed (e.g. from Kiln Client), set the following File->Settings (Commit and Sync tabs):
      • Check if your username <email> is correct (Kiln Client also saves your password after the first entry to make it easy to pull and push in the future, if this doesn't work it is best to reinstall Kiln Client)
      • Use Summary Line Length: 100
      • Push after Commit: default (this way after each commit you automatically push and don't forget it, very useful)
      • After pull operation: update (so you are always on the current version after you pull, super useful as well, you can still manually update to any revision if you like)
      TortoiseHg Settings
  • ReSharper is used to enforce most code rules defined here and of course for refactoring, manual testing and the following extensions make it even better:
    • CleanCode (most important extension, has about 10 concepts of Uncle Bobs Clean Code book)
    • ReCommended (newer extension for even more solid code analysis improvements and context actions)
    • CyclomaticComplexity (in addition to CleanCode a very important extension to make sure methods never exceed complexities of over 20 instructions)
    • TestCop (easily switch between unit tests and code, some extra clean code rules like tests matching class names)
    • ReSharper Helpers (smaller tweaks to improve productivity)
    • Enhanced Tooltip (colorizes parameter tooltips)
    • MoqComplete (very useful when writing Moq code, see this youtube tutorial I made to learn more)
    • FluentAssertions (lots of useful extensions to write more fluent style TDD code)
    Again: Always use DeltaEngine.Global.sln.DotSettings) as the basis for all projects for the most up to date code rules (current version is always checked into the default branch of the DeltaEngine repo).
  • NCrunch for continuous testing and coverage, 100% coverage is needed to pass TeamCity builds, this is by far the best and most important VS extension for any test driven developer!
      Use automatic testing of all tests unless ignored and go to Extensions->NCrunch->Set Engine Modes->Customize Engine Modes, select "Run all tests automatically" and go to filters at the very bottom and edit "Run automatically". Now make sure "doesn't have category" has "Slow" and "Nightly" set in 2 filters.
  • We use FogBugz as our planning and bug reporting tool. FogBugz works great with external contribution, freelancers and bug reports. You can submit bugs or feature requests directly and participate in the wiki there, with or without an account.
  • dotTrace and dotMemory for profiling (rarely needed or used, but if performance is bad and no obvious things in NCrunch or testing point to the issue, this will help a lot)
  • dotPeek to see inside assemblies and figure out APIs (not needed much nowadays)
Important files for new repositories (mostly what we use internally), you can get them from this google drive link:
  • .editorconfig (always useful for all editors, contains tab settings, 2 spaces per tab, line endings, limits, some basic code rules)
  • .hgignore (for hg repos)
  • .gitignore (for git repos)
  • DeltaEngine.Global.DotSettings (ReSharper inspectcode settings basis for all of our repositories, use it as a computer base layer, has support for all the plugins mentioned above)
.editorconfig content: ; EditorConfig to support per-solution formatting. Use the EditorConfig VS add-in to make this work. root = true [*.cs] dotnet_style_qualification_for_field = false:error dotnet_style_qualification_for_property = false:error dotnet_style_qualification_for_method = false:error dotnet_style_qualification_for_event = false:error dotnet_style_predefined_type_for_locals_parameters_members = true:error dotnet_style_predefined_type_for_member_access = true:error dotnet_style_object_initializer = true:error dotnet_style_collection_initializer = true:error dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_coalesce_expression = true:error dotnet_style_null_propagation = true:error csharp_style_var_for_built_in_types = false:error csharp_style_var_when_type_is_apparent = true:error csharp_style_var_elsewhere = true:error csharp_style_expression_bodied_methods = true:error csharp_style_expression_bodied_constructors = true:error csharp_style_expression_bodied_operators = true:error csharp_style_expression_bodied_properties = true:error csharp_style_expression_bodied_indexers = true:error csharp_style_expression_bodied_accessors = true:error csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion csharp_style_throw_expression = false:suggestion csharp_style_conditional_delegate_call = true:error csharp_indent_switch_labels = false csharp_indent_labels = one_less_than_current csharp_prefer_simple_using_statement = true:warning csharp_using_directive_placement = outside_namespace:warning deltaengine_max_method_lines = 15 deltaengine_max_parameters = 4 deltaengine_max_fields = 12 deltaengine_max_methods = 30 deltaengine_max_public_methods = 10 deltaengine_max_class_lines = 400 deltaengine_max_nesting_depth = 4 [*] end_of_line = CRLF indent_style = tab indent_size = 2 max_line_length = 100 trim_trailing_whitespace = true charset = UTF8
Use the default keys for Visual Studio and Resharper. Additionally we use F4 to Start the app much quicker than F5 (F5 is default for Start with Debugging) and F6-F9 for quick and easy testing. Make sure this is always setup on all PCs in the office so everyone can quickly get into working mode when pair programming.
  • F4: Debug.StartWithoutDebugging (also Ctrl+F5 by default, by far the most important hotkey to set in Visual Studio!)
  • F6: ReSharper.ReSharper_UnitTestRunContext (start test under cursor with Resharper, same as Ctrl+U,R, just much easier to press)
  • F7: Build.BuildSelection (Shift+Ctrl+B to Build Solution by default), not needed as much anymore because NCrunch compiles and tests everything on every keypress anyway.
  • F8: ReSharper.ReSharper_UnitTestSessionRepeatPreviousRun (useful for visual tests after fixing stuff, same as Ctrl+U,U)
  • F9: ReSharper.ReSharper_UnitTestDebugContext (Debug test under cursor with Resharper, also Ctrl+U+D)
  • Don't forget about other useful VS hotkeys, especially F12 (Go to definition), Shift+F12 (find all references), Alt+Enter (Fix ReSharper issue) and Ctrl+. (Intellisense) are important.
Tests are always written first, test classes end with Tests and are in a separate project.
This is VERY important, we have a weekly podcast where we try to train all employees and developers on how to write clean C# code. This is NOT easy, especially game, high performance or research code is extremely hard to write test driven first. You can also see that almost all of these coding guidelines are just about writing tests and how to structure your projects and code.

Tests projects should not reference other Tests projects to keep projects testable independently.
Mocks usually don't belong into Test projects (except when using Moq or just using the Mock once in a test class), create a separate Mocks project to make all the mock implementation available for all tests and other projects (e.g. database mocks, file mocks, graphics mocks, implementation mocks of external code, etc.)

Always follow Clean Code and the TDD rules:
  • 1. Write test code before production code. Work together with a team member to learn, it will require a lot of discipline and training.
  • 2. Make tests work and only write what is necessary for the tests to work (no extra code, put research into extra tests or extra projects).
  • 3. Refactor and make sure to get 100% coverage.
There are 3 kinds of tests, but all of them can be tested the same way (automatically via NCrunch (highly recommended), visually via ReSharper, or manually via Program.cs). With theTestWithMocksOrVisually class and the [Test] attribute you can easily combine automated tests and visual tests, with the [Test, CloseAfterFirstFrame] attributes you can mark tests as integration tests that will close immediately after running one frame. Both avoid duplicated code as everything in them is shared and tested automatically with Mocks using NCrunch all the time. This is especially true for integration tests, in particular graphics, multimedia, input, etc. tests, which need to be run with each framework separately. The same idea and implementation can be used in other high level projects too, no matter if testing robots, neural networks, image processors, cuda, games, database code or anything else that is slow to manually test and can be made fast with automated NCrunch tests. TeamCity will run both versions on each nightly build to make sure things still work all the time.

NCrunch will make sure that the overall logic works, manual testing is still required to confirm things are visible on screen, sound is working, etc. You can however also test all tests with ReSharper similar to our integration server, this will launch all tests and make sure they work (including making approval screenshots and comparing the result, quickly telling you if something broke).
public class DeviceTests : TestWithMocksOrVisually
  [Test, ApproveFirstFrameScreenshot]
  public void DrawRedBackground()
    Resolve<Window>().BackgroundColor = Color.Red;
  public void SizeChanged()
     Resolve<Window>().ViewportPixelSize = new Size(200, 100);
     Assert.That(Resolve<Window>().ViewportPixelSize, new Size(200, 100));
Not only does this test all the code with NCrunch automatically, but you can also let NCrunch test all integration tests automatically for you when setting TestStarter.NCrunchIgnoreSlowTests = false;. This is normally annoying when actually working on code, but still great for code reviews and testing functionality of hundreds of tests quickly before starting to confirm them visually as well.

ReSharper (via F6, F8 or F9) or other test runners also work great together with the [Test] attribute, which utilizes the resolver in the current framework you are using (e.g. TestWithGLFW3 uses GLFW3Resolver in its TestWithMocksOrVisually class).

Visual Tests

Graphics, UI (rendering, scenes) and IO (database, files, content) code is usually not automatically testable, but we still write functional tests first and try to separate UI code from the logic behind, which can be tested automatically and be refactored without impacting functionality of the UI code. Do not check in slow test code. Instead use Mocking (see Moq) to test slow or external calls and provide functional tests (with the Ignore attribute) for testing the whole functionality (no mocks) manually. See the next section about Mocks to see how to add fast tests without having to wait for external dependencies or slow code.
Native classes from external frameworks should not be derived to reduce dependencies (Windows Form, Graphics Device, Sound, native frameworks), always try to build a wrapper class around it! Otherwise we have a dependency we cannot test (tests would create forms, graphics, sockets, framework classes, etc. which is slow and sometimes not even automatically testable). This even goes beyond external frameworks, but also includes .NET framework classes providing external functionality like file handling, databases, xml.

All public methods should have tests (you probably need more for 100% coverage, or just remove unused methods). Functional visual tests (rendering, multimedia, input) are in classes derived from TestWithMocksOrVisually and accessible through the Editor.

A good strategy is:
  • Write automated test first.
  • Sometimes you need visual feedback, use TestWithMocksOrVisually.
  • After your task is completed, make sure each test runs below 10ms, otherwise put them to the integration tests [Category("Slow")] which are excluded from NCrunch but run nightly instead.

Note: Obviously write tests first and hold them to the same standard as production code. This also makes following the guidelines easier because you will most likely not write clean code at the first attempt. Instead you need to refactor it a lot and make it more beautiful as you go on. You will need unit tests that are executed whenever the project compiles to stay safe and be aware of all issues your refactoring might have brought in.

Mocking is not easy, especially not for beginners and if the code is not written in a test driven manner or if the code comes from an outside framework that is generally hard to test. Here is a quick youtube tutorial on Moq, but writing a simple Mock class is super simple with ReSharper, most of the time all you need to do is to press Alt+Enter on an implementation based on an interface you needed for your TDD code anyway.
Writing mocks is often misunderstood and not liked by many (even professional) programmers because it ads lots of extra code and overhead to your code base without any visible benefits to production code. Mocking is however crucial to testing and the code that is required to make code mock-able is usually required to get rid of dependencies anyway and will make the code much more stable, robust and easier to change because of tests that run all the time (via NCrunch). Visual and functional tests are great to verify UI and graphical behaviour, but you cannot run all visual tests in a project for every little change. You can however let NCrunch run all the tests all the time in the background! The most important aspect of mocking is making slow and external code run fast by just not executing it at all. Also only with Mocks it is possible to accomplish 100% automated code coverage (except for stuff that is mocked away like external frameworks and slow code, which we usually test with automated integration tests that run nightly).

Most mocks are just stubs or fake objects to be able to test quickly without invoking slow framework methods (IO, GPU, API calls). Some times a spy is also useful to inspect inner data. For this reason mock, fake and spy classes can break some rules defined below for easy access (public static). For details see

So mocking excludes functionality (external code, slow code, i/o code, etc.), which has the advantage to quickly test if your code is doing what it is supposed to do, but this obviously does not test if the code makes sense at all or even works correctly with external frameworks or IO. Many things can still be tested automatically via Integration tests (done in our engine via [Test, Category("Slow")]), which just run the slow code (on demand and also automatically on our build server).

This still does not mean the code make sense, we just made sure every line of code is executed and does not crash and all the results are as expected. This does however not test if there was something visible on the screen, if the application makes sense or if a game is fun. Especially for UI, Graphics, Behaviour and testing your application functionality from the user perspective functional tests are required, which is usually just the Test class duplicated again, but using an Ignore attribute. These tests are not automatically executed by NCrunch, TestDriven.NET, NUnit or ReSharpers Test Runners, but if you execute them manually one by one you can still execute them and all mocking from the engine will be automatically disabled so you see the real behavior.
You will see many times in the engine and sample games test projects that the same code is executed with and without mocks. This way we can reach 100% test coverage and make sure every single line of code is really executed both automatically and when actually running the application.
public void StartProgramWithMocks()
[Test, Ignore("Visual")]
public void StartProgramNormallyAsVisualTest()

You can download any source code file to understand our Coding Style. Please always follow the existing convention. The following example has extra documentation because the functionality cannot be made 100% clear with just the name.
/// <summary>
/// Converts positions into easy to use 0-360 degrees (top=0, right=90, bottom=180, left=270).
/// </summary>
public float GetClockwiseUnitRotation(Point unitCirclePosition)
    if (unitCirclePosition == Zero)
        return 0;
    float rawAngle = MathHelper.Atan(unitCirclePosition.X, -unitCirclePosition.Y);
    return rawAngle < 0.0f ? 360.0f + rawAngle : rawAngle;

Each file should contain only one type, no matter how small the class, struct or enum is. Also no nested types are allowed except for exception classes or internal structures not needed outside (e.g. for native code), which are easy to exclude or ignore.

Each line should be a short as possible (around 40 characters), long lines can have 80-100 characters. 98 is the setting for wrapping long lines with ReSharper (because it does not count tabs as 2 characters). 101 is where our guideline is shown, use the Editor Guidelines package! All of our tabs have this width and we have horizontal scrolling disabled. On smaller monitors this fits okay, on big monitors (1920px width upwards) two tabs fit nicely.

The following structure is used in all files:
/// <summary>
/// Description what the class is designed for (in general) and how it works and how the class is implemented (concretely).
/// Be precise and try to do it in one line, max. 3 lines! Only describe, do not repeat obvious names or functionality.
/// </summary>
public class PizzaFactory
    // Always start with the constructor or first method that will be called (driven by TDD).
    // Then list all the fields and helper methods for that method recursively until done.
    // Then continue with the next public method (again driven by writing tests first).
Avoid public constants, try to use them only in your assembly (only Pi is allowed because it never changes). For details see this and this.

Tests are always in a separate .Tests project that can be excluded easily.
public class PizzaFactoryTests
    // Unit tests use the [Test] attribute, integration test use [Test, Category("Slow")] and
    // visual and functional tests are marked with [Test, Ignore] and have to be executed manually.

Projects should always use the .NET 5 project format to use the latest and greatest C# has to offer, we will switch to .NET 6 once it is out. .NET 5 also runs great on many systems, not just Windows. We don't want to worry about 64bit, but all of our code is always used on 64bit platforms nowadays, so if there is native code, we prefer 64bit (e.g. our nuget packages providing support for graphic frameworks, image processing, cuda, etc. are all 64 bit native code).

When adding References outside DeltaEngine references you can use the typical Alt+Enter "Add Reference" shortcut with ReSharper.

New projects created should be put into correct folder structure:
  • \DeltaEngine\DeltaEngine.Graphics\DeltaEngine.Graphics.csproj
  • \DeltaEngine\DeltaEngine.Graphics.Tests\DeltaEngine.Graphics.Tests.csproj
Notice that the project name, folder name and namespace name always match!

Never write any static class, except for extension classes. Static methods should only appear for Extension classes or private helper functions.

Use var when creating a type in any method. Then the variable type is already know (instance or value type are handled the same way). ReSharper will automatically suggest this:
var position = new Point(1, 2);
int number = 5;
string helloWorld = "Hello World";
string[] splittedHelloWorld = helloWorld.Split(new[] { ' ' });
var customTexts = new string[] { "Hi", "there" };

Negate if to make source code more clear:
public virtual void Dispose()
    if (container == null)
    container = null;

Try to avoid false checks, if this is not possible always use the negate operator "!" instead of writing " == false", e.g.
if (!ContainsText("Hello"))
AddText("Something else that is always added");

Use lambdas to simplify work, use the ReSharper code issue rules to guide you (single statements should always be lambdas and be fit into one line).
class Program
    new Command(Command.Click, position => new Line2D(Point.Zero, position, Color.Teal));

Another good way to simplify code is to use ??, which is especially useful for caching:
public Generator Generator => cachedGenerator ?? (cachedGenerator = GetNewGenerator());

All file structure and indentation settings are always set in the .editorconfig file (see above for download) in the main folder of any solution or repository. All editors respect these settings (tabs, newlines, etc.).
All code should be indented with tabs using 2 spaces (in VS settings use smart tabs, 2 spaces per tab, keep tabs always). Horizontal scrolling is forbidden (see above, lines should be below 80 characters, max. 100).

Brackets should only be needed at method or property level, everything else should fit in one line. A blank line should be added after each logical block (ifs, fors, method groups). Exceptions are do-while loops, switch, try, catch and finally, which all require brackets by the language design, but still try to keep the code in them very compact (1 line if possible).
if (text == null)
    return "no text";
for (int index = 0; index < text.Length; index++)
    if (text[index] == '!')
        return text.Substring(0, index);

Note: Checking for null should be avoided, unit tests should check for this not to happen. Sometimes it still is required for high level public engine methods because we don't know what users will pass in.

In order to keep code short and simple, try to use linq queries and extension methods when collections are iterated and something is returned (a boolean, a string or another collection). Do not do this for normal enumerations (foreach and calling a method with each instance) even though ReSharper will suggest it. Do not worry too much about performance, you can still revert this when profiling and remember that the Delta Engine services will automatically optimize all possibly slow code (including linq queries).
Instead of:
foreach (string name in names)
    if (nameToCheck.StartsWith(name))
        return true;
return false;
rewrite it with the Any extension method as ReSharper will suggest:
return names.Any(name => assemblyName.StartsWith(name));

Here is a more complex code block that can be replaced with an easier to understand linq query:
var runners = new List<:Runner>();
foreach (Type type in assembly.GetTypes())
    if (IsNonAbstractRunner(type))
        runners.Add(resolver(type) as Runner);
return runners;
Which can be turned into this simple linq query (notice the linq query formatting):
    from type in assembly.GetTypes()
    where IsNonAbstractRunner(type)
    select resolver(type) as Runner;

But there are also examples of code that should not always be changed to linq as it does not get easier to read at all. Whenever the code gets longer by converting into a linq query, line lengths explode or when there is nothing to return and it reads nice as is, the code should usually stay as it is.
foreach (var type in types)
    if (!type.IsAbstract)

Constants and fields of classes or structs should always be kept to a bare minimum. A good number is 0-5 private fields, maybe 10-15 for complex classes, anything above that needs serious rethinking! Every time you use a struct inside a class and need public access for modification to it (e.g. DrawArea, RotationCenter, Matrix, etc.), it makes sense to provide it as a public field
/// <summary>
/// Keeps position, size and color for an automatically rendered rectangle on screen.
/// </summary>
public class ColoredRectangle : Renderable
    public ColoredRectangle(Renderer render, Rectangle rect, Color color)
        : base(render)
        Rect = rect;
        Color = color;
    public ColoredRectangle(Renderer render, Point center, Size size, Color color)
        : this(render, Rectangle.FromCenter(center, size), color) { }
    public Rectangle Rect;
    public Color Color;
    protected internal override void Render()

All class instances are either created by the Resolver (inside App) or in code an then injected into other classes. The only exception is the Scene class because we always need access to content files, patterns and entities stores in the Content folder and managed via the Editor. Content is managed through a scene stack and we only work at the top most one, e.g. in games each level and the menu have their scenes. Scenes can use content files, patterns and entities from parent scenes as well.
There should be a maximum of 3 constructors (use default parameters and derived classes for extra functionality). See file structure
In case you need to assign to fields or properties, use this to assign the constructor parameter value to the field:
public LogoApp(Image image)
    this.image = image;
private Image image;
public void Update()
    image.DrawArea = Movement * Time.Delta;

Public methods are also limited by 10 per class, private methods are not limited, but a class should be as short and concise as possible (around max. 200-400 lines). Each public method should be followed by the private methods that are called in it in the same order.

Each method should do one thing, and only have zero or one parameter. If required use two parameters, three should be the absolute limit. The same is true for constructor parameters, split up your functionality into several classes or use Property Injection if you need access to more things. The only exception should be constructors of manager type classes (but don't name them manager or helper), for example it is perfectly okay for the Input class to get injected by all kind of devices because that is what this class is for. Doing one thing means:
  • Methods called Get, Is, etc. should only grab data and not modify the instance at all! The should also not "do" anything except return existing values. Using simple calculations, ifs or dictionaries is okay.
  • Other methods should do something with the instance based on the passed in parameters (e.g. Save(filename) will save out the data to disk, Update will update a game tick) and not return anything.
  • Only Create or Load methods should actually create new instances and return them (e.g. Content.Load or BinaryFactory.Create)
  • Keep methods short. One method should not do all initialization, all loading, all conditional logic and all rendering functionality, split it up into multiple methods and call them all in your method (then it still does the same thing, but nothing of the original "doing" is left, the method has a new purpose now to call multiple low level methods).
  • Exception handling should be in its own method, it should only have one line to try something and the exception handler block (usually one line as well, max 3-5 lines per handler block, max 3handlers). See an example below.

Normally a method should only need 3-5 lines of code, the limit is 10 lines of code. The absolute limit is 20 lines and this should only be done in private methods and when you cannot avoid it (when switch statements or legacy code handling is required). Code reviews are essential to keep the code this simple! Comments are rarely needed for methods and it is usually a violation if you see any comment inside a method, especially for if statements or method calls. Try to incorporate the comment into the method call and simplify.

All preprocessor directives are to be avoided:

 As an alternative use ExceptionExtensions.IsDebugMode

Enums should be short, names should be descriptive and often do not need extra comments. Enum values should be singular: Key.A, Key.Space

Properties should describe exactly what they do in the name and a xml comment should never be needed. If a property does more than just return a value or when it is not clear what it does, consider using a method instead. 10 is the maximum for public properties in any class (including public fields).

General rules from Entity Component System should be applied (if not you will get an exception anyway). For example all Component and EntityContent classes just have properties, no functionality. Some rare components have a public static readonly Shared field because there is always only one instance (Inactive, RenderData, Gravity2D, GizmoEditMode). Also generated code (e.g. Content.cs) can have public static helper properties to simplify access to external resources in a statically typed safe way.

Sometimes it is useful to specify extra optional functionality in a class via a property like Logging, Randomizing, Access to other dependency injected classes, etc. If the class cannot do its job without the dependency, then add it to the constructor (not doing this would result in not fully initialized classes, which is never good). However sometimes you only rarely need certain functionality like Logging, which only is used if something goes wrong and you want to support both not logging at all (leave the Log property null) or to Log in the way you have setup somewhere else (e.g. to a log service, to a file, to the console for tests, etc.). The same could be said for a number of low level utility classes (Time, Random, File, IO, Networking, Profiling, Xml, Math, Json, Cryptography, etc.). Additional reading:

If your class depends on something, make sure you inject the dependency in the constructor, otherwise you may use Property Injection for optional functionality or things the class can resolve on its own. Here are some examples (also shows how to use exception handling in methods):
public Log Log { get; set; }
public void ExecuteCrazyThing()
    catch (CrazyException ex)
        if (Log != null)
        throw ex;
private void TryExecuteCrazyThing()

All constants (including public static readonly, which is only allowed for value types), methods, public properties and public fields (usually of of struct types or when using [FieldOffset]) should be upper case:
public float Center
    get { return TopLeft + Size / 2; }
    set { TopLeft = value - Size / 2; }
public float X { get; set; }
public float Y { get; set; }
public const float Pi = 3.14159265359f;
public static readonly Point Zero = new Point(0, 0);
public Point Position;
public Rectangle DrawArea;

All other fields or properties should be lower case (it should be rare writing a private only property, protected properties might exist, but should be rare too):
protected Socket socket;
private Factory factory;
private Texture2D texture;      
private SamplerState samplerState;

Only write them when really required, try to describe with method names if at all possible (comments should be avoided at all cost). The absolute limit is 2 lines of comments, no TODO, HACK, TOCO, etc. are allowed (all tasks have to be in our project management system, not in source code)! If you want to write more and detailed explanations create a FogBugz wiki page and link it up in the xml summary. Also commented out code is not allowed when checking in. Check in your code often, so you can always revert. Also keep the code clean of any header (only leave legal notes in if required) and do not post any user comments, notes or time stamps. Keep the source code clean and professional.

Basically the best code you can write does not need any comments, except for the class summary you will find many classes in the Delta Engine that have no comments at all and explain what they do well in code already.

All public engine classes need an xml comment for the help generation! If at all possible stick Xml summary comments into one line (see examples above). The maximum amount is 3 lines, only add extra parameter information if you have something useful to say for them. Optionally public methods can have comments too, but it is not required. Everything in a source file should be as small as possible, leave out useless comments! For detailed documentation about high level concepts check this website and ask any employee.

Regions are only allowed for hiding auto-generated code (rarely ever happens nowadays with non-windows-forms). There should be no reason to use regions, you can collapse methods directly and source files should be small anyway.

  • Reuse external libraries and code as much as possible. If it does not work, try a different library. If nothing works, reinvent the wheel :)
  • Reuse existing engine code, use what classes are for, use helper method, use existing delegates, etc.
    This is also true if custom delegates can be avoided, e.g. use Action for simple delegates and Action<Type> for delegates with return values, e.g. instead of defining your own custom delegate for size changes just use an Action:
    event Action<Size> SizeChanged;
  • Always collapse single line delegates into lambdas (see the example file from above)
  • If you are unsure ask a fellow Delta Engine developer.

  • This seems obvious but remember that an ‘Event’ is always something that has happened. It should therefore reflect this in it’s name being in the past tense. For more details about event streams, DDD and CQRS, check the wiki.
    In tools, websites, client side code that end users can see always use Localization for strings. It is okay to have just English error messages if the target is a programmer anyway (e.g. an exception an end user should never see). Obviously try to use enums and just exception class names without messages internally and only present the error to the user or programmer at a higher level.

    Always use string concatenation with the + operator. String.Format is not only harder to maintain, ugly and error prone, but also slower. It still might be a good choice for a variable params list, but that should be the only place were you should use String.Format. Here are a few hard to understand examples:
    string.Format("sign /sha1 {0} \"{1}\"", this.CertificateThumbprint, this.XapOutputFile);
    string signArgs = string.Format(
      "-keystore {0} -signedjar \"{2}\" -storepass {1} -keypass {1} \"{3}\" {4}",
      keystorePath, keypass, signedApk, packageFile, keyAlias);
    This type of code should be avoided. Anything longer than a line of code (max. 100 characters) is already a smell. Instead write command line code into little helper methods to be reused many times.

    Details about Entities, Triggers, Commands (all recommended to be build and setup via the Editor) and how high level code (like Processors for Entities) should be written can be found in our wiki:

    • Check if all code still follows the basic rules of clean code via ReSharper, NDepend and NCrunch, see our Build Server.
    • Additionally you can check out the old 2013 coding style for more details, still 90% true, but outdated on some tools and tips.