Sphere 10 Software employs it's own code-styling approach which all contributions must adhere to. This document provides styling guidelines for common situations. To help contributors, Visual Studio and Resharper code-styling definitions are also provided and which can be adopted quickly by simply copying the .editorconfig
file to the solution folder. However, the guidelines in this document go beyond what can be auto-formatted and contributors are required to read, understand and apply these guidelines.
Visual Studio & Resharper Editor Config)
NOTE: This document is still evolving and contributors should refresh themselves periodically.
NOTE: If a styling element / aspect is not addressed by this document (or editor config) then the contributor has artistic license by default.
Braces should begin at end of line and finish at beginning of line (with corresponding tab offsets)
public class SomeClass {
public void SomeMethod() {
if (condition) {
if (condition1) {
foo1();
foo2();
} else {
foo3();
foo4();
}
}
}
}
The naming conventions should follow the default C# conventions as suggested by Visual Studio IDE and/or Resharper intellisense. These include
- Pascal-cased naming convention for all field, argument, variable members.
- Capitalized for all public, protected and internal members.
And most importantly 3. Members are named using "self-describing" names so that the code is a "living documentation". This means generally avoiding short, cryptic and abbreviated names.
Example:
public class PascalCasedClassName {
private int _privateFieldName; // private fields start with underscore
protected int NonPrivateFieldName; // non-private fields start with capitals
public PascalCasedClassname() { } // Pascal-cased constructor
public int PrivateMethodName() { } // Pascal-cased private methods
public int PublicMethodName() { } // Pascal-cased public methods
}
Class members should be ordered according to the following guide
public class SomeClass {
// 1. Event declarations
// 2. Private field declarations
// 3. Protected field declarations
// 4. Constructors (basic constructors first, complex constructors last)
// 4b. Finalizers, if any
// 5. Public properties
// 6. Internal/protected/private properties
// 7. Public methods
// 8. Internal/protected/private methods
// 9. Inner class/type declarations
}
Namespaces are NOT necessarily organized by the strict ProjectName.folder.subfolder.subsubfolder
convention suggested by Visual Studio and Resharper. Care must be taken by the developer to ensure the namespaces are logically thought out and not overly granular. Expecting framework consumers to know the namespaces can be unrealistic and the preference is to employ more granular namespaces.
For example, the Hydrogen
module contains a myriad of system-tier domains which are all encumbered within a single Hydrogen
namespace (this may change in future).
For system-tier modules that is acceptable, however for other tiers the namespaces should be decomposed into a logical structuring that matches the CompanyName.ProductName.Tier.Domain
pattern. If a domain is decomposed into sub-folders then it does NOT automatically mean the namespace should also be decomposed that way. It's up to developers discretion. Please refer to Sphere 10's 3-tier architecture guidelines for understanding how tiers, domains and modules are structured according to the Sphere 10 Software Engineering methodology.
Example
Hydrogen
Hydrogen.Application
Hydrogen.DApp.Core
Hydrogen.DApp.Core.Kademlia
Hydrogen.DApp.Presentation.Host
Hydrogen.DApp.Presentation.Node
Hydrogen.DApp.Presentation.UI
Hydrogen Framework employs a syntactic naming convention for providing "tool" classes to framework consumers. Tools are static classes that provide functionality in a specific domain. What makes them different is that tool classes are always defined in the global::Tools
namespace and never imported directly.
The reason it is done this way is to that framework consumers can always pull up tools by simply relying on intellisense and by typing Tools.
. Since "Tools" is a global namespace, all classes defined in Tools will appear. This makes it very easy for a developer to pull up the toolbox whenever it is needed. Sphere 10 and Hydrogen framework Since knowing and remembering all tools is an unrealistic thing, tools can always be brought up by intellisense.
To use tools, simply ensure the assembly is referenced and type Tools.
in the code editor:
To create a tool, simply create a static class anywhere in the module and ensure it's namespace is simply Tools
. Example:
namespace Tools {
public static class Enums {
public static IEnumerable<T> GetValues<T>() {
return Enum.GetValues(typeof(T)).Cast<T>();
}
/// add more static methods here
}
Invocation of base or sibling constructor should always be on the next line
public Constructor(int arg1, string arg2) : base(arg1, arg2, "newArgValue")
becomes
public Constructor(int arg1, string arg2)
: base(arg1, arg2, "newArgValue")
Explicit type declarations should be avoided where possible in preference of var
.
Example:
IEnumerable<ISomeInterface<string>> sequence = GetSequence();
foreach(ISomeInterface<string> item in sequence) {
//...
}
becomes
var sequence = GetSequence();
foreach(var item in sequence) {
//...
}
Single-line scopes should avoid using begin/end braces.
if (condition) {
foreach (TItem item in items) {
Serializer.Serialize(item, Writer);
}
}
becomes
if (condition)
foreach (TItem item in items)
Serializer.Serialize(item, Writer);
Redundant else's should always be removed.
if (condition) {
//...
return true; // this could also be a throw
} else {
_includeListHeader = value;
}
becomes
if (condition) {
// ...
return true; // this could also be a throw
}
_includeListHeader = value;
A line of code should not be wrapped unless doing so significantly improves readability. As a guide, the following rules should be employed:
-
It has gone over 170 characters on the line;
-
The line is complex and invokes functions/constructors/property setters, etc. These scenarios are typically when constructing an object graph or using a LINQ query.
The following situations by themselves do NOT warrant wrapping:
-
Many arguments
public Method(int arg1,
string arg2,
string arg3,
IList<int> arg4
.
.
.
)
should be
public Method(int arg1, string arg2, string arg3, IList<int> arg4, ...)
unless the argument count was gratuitously long then it can be 1-arg per line.
When wrapping a line after an open-parenthesis (
ensure the close-parenthesis )
is placed on new line at the indentation of the line which originated the (
(i.e. as if it were begin/end braces {
}
).
Example:
if (condition) {
SomeComplexMethod(
SomeArgument1,
SomeOtherMethod(
SomeArgument2,
SomeArgument3));
}
becomes
if (condition) {
SomeComplexMethod(
SomeArgument1,
SomeOtherMethod(
SomeArgument2,
SomeArgument3
)
);
}
Indentation for code should be tab-based with 4-character length whereas indentation for comments should be space-basee not tab-based.
Example
[Flags]
public enum SomeEnum {
SomeVal1 = 1 << 0, // Line starts with tab but = and comments are spaced
SomeVal2 = 1 << 1, // comment
}
Developers should add code-comments inside their code so as to assist auditors on what the purpose of the code is. XMLDOC code comments need not be done during development but can be retrofitted later using tooling. Spaces between lines of code should only be used to group the "logical segments" of the code block. All code within a "logical segment" should have no spaces. Each logical segment should have a comment.
Example:
public void Foo() {
var x = 10;
var y = GetSomeValue(x);
System.Console.WriteLine(y);
var obj = new SomeObject {
Prop1 = x + y,
Prop2 = 11
}
var z = CallMethod(obj);
return z;
}
should be
public void Foo() {
// Comment for logical segment 1 of code block
var x = 10;
var y = GetSomeValue(x);
System.Console.WriteLine(y);
// Comment for logical segment 2of code block
var obj = new SomeObject {
Prop1 = x + y,
Prop2 = 11
}
var z = CallMethod(obj);
return z; // note: the space before return denotes it is separate from both segments
}
This rule also applies to class declarations, methods, etc. Only 1 single line of space between
namespace SomeNamespace {
// Notice line (OPTIONAL)
public class SomeClass() {
// Notice line
public Method1() {
/// ...
}
// Notice line
public Method2() {
/// ...
}
// Notice line
}
// Notice line (OPTIONAL)
}