This guide is for developers who wish to contribute to the AsmResolver codebase. It will detail how to properly style and format code to fit this project.
Following this guide and formatting your code as detailed will likely get your pull request merged much faster than if you don't (assuming the written code has no mistakes in itself).
If you make any changes to AsmResolver, you are agreeing to the license conditions as specified in LICENSE.md.
The AsmResolver project generally follows the principles of Git Flow, with a few variations. Below a summary:
- Prefer to create a branch based on
development
.- Do not branch from
master
unless it is a serious bug and requires a hotfix.master
is supposed to be always in sync with the nuget feed.
- Do not branch from
- Prefix your branch accordingly, depending on what kind of change you are trying to make.
- For new features, use
feature/name-of-feature
. - For issues and/or bug fixes, use
issue/name-of-issue-or-bug
.
- For new features, use
- Push your changes on this branch.
- Make sure you are following the coding style guidelines as described in this document below.
- Open a Pull Request, setting the
development
branch as a base branch to merge into. - Wait for your pull request to be reviewed and accepted.
- Pull requests into
development
will only be accepted if all unit tests succeed and follow the guidelines as described in this document.
- Pull requests into
The general idea behind the coding style that is adopted in the AsmResolver project follows a few key points:
- Do not to be afraid to be verbose.
- Separate different parts of the code from each other wherever possible.
Furthermore:
- Use 4 spaces for indentation.
- Try to limit the number of characters on one line to 120 characters.
- Avoid preprocessor directives or
#region
s
For editors that support EditorConfig, there is an .editorconfig
file for you to use in the root directory of the repository.
Below an overview of the naming conventions used within the project.
Member type | Naming style |
---|---|
Namespaces | PascalCase |
Classes | PascalCase |
Structs | PascalCase |
Interfaces | IPrefixedPascalCase |
Private instance fields | _camelCaseWithUnderscore |
Any other field | PascalCase |
Methods | PascalCase |
Properties | PascalCase |
Events | PascalCase |
Parameters | camelCase |
Local variables | camelCase |
Local constants | camelCase |
Avoid truncating any words within the name of a member:
Do:
public class ModuleDefinition
Don't:
public class ModuleDef
In the case an abbreviation is still used, only capitalize all letters of the abbreviation if the abbreviation is at most two letters. Otherwise, only capitalize the first letter in the abbreviation.
Do:
public class PEImage { ... }
public struct CilOpCode { ... }
Don't:
public class PeImage { ... }
public struct CILOpCode { ... }
In the case of an interface name starting with an abbreviation, we do not count the prefix I
when counting the letters of an abbreviation.
Do:
public interface IPEImage { ... }
Don't:
public interface IPeImage { ... }
public interface IpeImage { ... }
The general order of members within a single file is as followed:
- Events
- Fields
- Constructors
- Properties
- Methods
- Nested types
Prefer to mark the current class
as partial
over using #region
directives when the file gets too large.
Always place opening and closing braces on a new line, and indent after.
Do:
public namespace N
{
public class T
{
public void Method()
{
Console.WriteLine("Hello, world!");
}
}
}
Don't:
public namespace N {
public class T {
public void Method() {
Console.WriteLine("Hello, world!");
}
}
}
Arms of an if
statement should always be wrapped using braces. The only place when it is acceptable to exclude braces is when all arms only consist of one line of code. In such a case, the embedded statement should be on a new line, indented.
Do:
if (x == y)
MethodA();
if (x == y)
MethodA();
else
MethodB();
if (x == y)
{
MethodA();
}
else
{
MethodB();
MethodC();
}
Don't:
if (x == y) MethodA();
if (x == y)
MethodA();
else
{
MethodB();
MethodC();
}
For loops, both including and excluding braces is acceptable, even if the embedded statement only spans one line. Similar to if
statements, when braces are excluded, always place the embedded statement on a new indented line.
foreach (var x in collection)
Method(x);
foreach (var x in collection)
{
Method(x);
}
Use var
for anything that is not a primitive type for which a dedicated keyword exists.
Do:
int x = 123;
string y = "Hello, world!";
byte[] z = new byte[10];
var instance = new MyClass(...);
Don't:
var x = 123;
var y = "Hello, world!";
var array = new byte[10];
MyClass instance = new MyClass(...);
Prefer to use the most general type of the returned object that still conveys the overal structure of the returned object, as much as possible. For instance, prefer using IList<T>
over List<T>
for mutable collection properties, or IEnumerable<T>
over List<T>
for methods that return a collection.
Do:
public IList<MethodDefinition> Methods
{
get;
}
public IEnumerable<TypeDefinition> GetAllTypes()
{
// ...
}
Don't:
public List<MethodDefinition> Methods
{
get;
}
public List<TypeDefinition> GetAllTypes()
{
// ...
}
For non-public members, using the more specific type is acceptable.
Only use this
when absolutely necessary. Prefer to omit it from any expression unless there is ambiguity or when this
needs to be used as an argument.
Do:
int x = _myField;
SomeMethod();
Don't:
int x = this._myField;
this.SomeMethod();
Prefer to place the arms of a ternary expression on separate lines:
Do:
var temp = condition
? MethodA()
: MethodB();
Don't:
var temp = condition ? MethodA() : MethodB();
Prefer for
loops over foreach
when heap allocated enumerators can be avoided. For example, if GetEnumerator
returns a non-struct type enumerator, but accessing elements by index is possible, prefer to use a for
loop with an indexer variable.
Do:
var items = assembly.Modules;
for (int i = 0; i < items.Count; i++)
{
// Use items[i]
}
Don't:
var items = assembly.Modules;
foreach (var item in items) // IList<T>.GetEnumerator() returns a heap allocated enumerator
{
// Use item
}
Using LINQ is acceptable, but prefer the method syntax over the query syntax. When multiple method calls are chained together, prefer to put them on separate lines:
Do:
var allClassMethods = assembly.Modules
.SelectMany(m => m.GetAllTypes())
.Where(t => t.IsClass)
.SelectMany(t => t.Methods)
.ToArray();
Don't:
var allClasses = assembly.Modules.SelectMany(m => m.GetAllTypes()).Where(t => t.IsClass).SelectMany(t => t.Methods).ToArray();
Always provide at least the <summary>
xml documentation tag for non-private
and non-internal
members.