Thank you for showing interest in this project. This document lists some of the guidelines I set out when I started or accumulated over the development process. I recommend that you read them if you are confused about the design philosophy of the project.
TODO TBD ;)
If you would like to make contributions to the code, consider this section carefully.
All contributions should follow the Code style that is laid out in this document. In addition, all contributions should provide appropriate tests in the shared test project. If you add functionality or types that are part of the broader interface, other documentation besides XML comments might be in order. Be sure to coordinate wiki contributions with me as well.
TODO TBD ;)
I strive to keep a consistent style of code in the codebase of the Whetstone.Core project. While some arbitrary choices have to be made where debating would be fruitless, others are open for change.
Here are some general rules that apply everywhere.
-
Annotate as much as possible.
Use the appropriate annotations from the
System
assemblies and those provided by theJetBrains.Annotations
package to annotate all code elements. Express invariants using them, and ensure correct and sensible behavior when debugging or analyzing the project. -
Prefer expression bodies.
Whenever possible, and where their length does not exceed one line unless they are a single binary/ternary operator or call, expression bodies shall be used. This includes properties, property accessors and methods.
-
Prefer
readonly
fields and value types.Wherever possible, ensure that fields and value types are immutable.
-
Prefer readonly references (
in
) for parameters that might assume a value type (includes generics). -
Use verbatim strings for untranslated messages.
To indicate that strings are messages that serve an indicative or development purpose, like exception and assertions messages, shall be provided as verbatim strings.
-
Prefer string interpolation.
Where format expressions are used, use interpolated string expressions (
$"..."
) whenever possible. If the previous rule cannot be fulfilled this way, use a string constant instead. -
Prefer extension methods.
If methods can be provided on top of existing interfaces or types, and do not conflict with existing names or overloads, use extension methods instead. Avoid them however if a certain method lacks complexity but has a high call frequency.
In order of significance, the following rules for what must be documented apply.
-
All
public
,protected
,private protected
andinternal
types and members require documentation. Onlyprivate
elements are exempt, unless required by another rule. -
All methods require complete exception documentation. This includes
private
methods, which therefore need to be documented if they can throw exceptions. -
Assertions that validate contracts that are not obvious from the signature and annotations (like
[NotNull]
) need to be documented in the<remarks>
section. They may only be present in internals, and they can also warrantprivate
methods to be documented.
In general, every method should perform contract validation. This includes the parameters, but also the state of the objects it operates on, unless it is unspecific in these aspects.
There are two recognized ways to check contracts:
-
Require.
*,ThrowIf
*The static methods provided by
Require
perform contract validation based on exceptions, while theThrowIf
* instance methods perform state validation also based on exceptions.Use this kind of validation on all public interfaces, for all values and states that can potentially be manipulated by outside (different assembly) callers.
-
Ensure.
*,Assert
*The static methods provided by
Ensure
mirror those inRequire
, as theAssert
* instance methods do theThrowIf
* methods. They perform assertion-based validation that is always[Conditional]
.Use this kind of validation for everything else, and only if there is no way to trigger them from outside code.
The names of all code elements shall adhere to the following rules.
Code element | Rule |
---|---|
Namespace | UpperCamelCase |
Class, Struct | UpperCamelCase |
Interface | IUpperCamelCase |
Method | UpperCamelCase |
Property | UpperCamelCase |
Event | UpperCamelCase |
Field (private ) |
FUpperCamelCase |
Field (static , private ) |
_FUpperCamelCase |
Constant | C_UpperCamelCase |
Enum member | UpperCamelCase |
Parameter | AUpperCamelCase , BUpperCamelCase |
Type parameter | TUpperCamelCase |
Lambda parameter | X , Y , Parameter |
Local variable | lowerCamelCase |
Local constant | C_UpperCamelCase |
Local function | UpperCamelCase |