Coding Standard - UberWaffe/OpenRA GitHub Wiki
As of release-20110511, this is the official coding standard, which should be used as part of the process for reviewing patches. It is based on the .NET conventions, which should be consulted where guidelines are missing here. You can use StyleCop to check your own code prior to submission.
For your convenience this repository supports EditorConfig to set up source code editors according to this projects format convention.
-
One class per file, except in very special cases. The only common exception is traits, which require a
Foo/FooInfo
pair. TraitsInterfaces is another prominent exception. The rationale here is that having a bunch of largely one-line interfaces all in one file is easier for mod authors. This might change at some point. -
Files should have the same name as the (primary) class they contain.
-
Don't create new dependencies between assemblies unless you have a really good reason for it.
-
Put classes in the namespace which fits their purpose best. A long-standing example of doing this wrong is putting client-side master server support in the
OpenRA.Server
namespace. It belongs inOpenRA.Network
.
-
Type names are PascalCase. If a typename contains an acronym longer than two characters, don't capitalize the whole thing. For two character acronyms, the correct case depends on context. GL, AL, etc should probably be capitalized; but Cg is always written with the lowercase 'g'. RA is capitalized by convention. Command & Conquer should be abbreviated Cnc.
-
All methods, properties, and events are PascalCase, regardless of visibility.
-
Public fields are PascalCase. There is nothing evil about public fields -- they are preferable to 'dumb' properties.
-
Private fields are camelCase.
-
Function parameters are camelCase. This allows the
Foo = foo;
pattern without requiring one side to be qualified withthis.
-
Local variables are camelCase.
-
The name for a lambda argument which you don't care about is
_
. For example,_ => Foo.Bar()
is a lambda function which ignores its argument. -
Spell typenames correctly. If the spelling varies between dialects of English, prefer the US spelling.
Color
, notColour
.Program
, notProgramme
. This is for consistency with the platform APIs, which are almost always US English. -
If a variable is going to be bound to yaml, it is important for it to have a descriptive name. In many other cases, you should not use long names, especially for locals.
-
Type parameters are
T
,U
,V
and so on. -
If there is one function you're talking about, it is reasonable to call it
f
. If there are two or three,g
andh
are perfectly good too. If you need four, you're trying to do too much in one function. -
s
is a reasonable name for a string, if there is exactly one; -
i
,j
,k
are perfectly good induction variables; -
If you have one value of generic type
T
, call itt
. If you have a sequence [IEnumerable<T>
], call itts
. If you have anActor
or anAction
or anActivity
, you can use often usea
with no loss of readability. -
int2
orfloat2
values are oftenu
orv
. -
An exception object should be named
e
orex
. If you don't actually want the object, don't give it a name at all [catch(FooException) {}
orcatch {}
]. In most cases you do want the object, at least to log.
- Assign to each local variable exactly once, at its point of definition. Use
var
instead of writing a typename if at all possible (there are a few cases where you can't).
-
Do not declare your own delegate types, unless it is impossible to use
Action<>
orFunc<>
. A rare but valid case where this is impossible is if a delegate type must take anout
orref
parameter. To obtain a delegate type which would matchvoid Foo(ref int x)
, you cannot sayAction<ref int>
, sinceref int
is not a valid type parameter. This occurs in the Blowfish implementation, don't treat it as a good pattern to copy. -
If you need pairs of things, use
Pair<>
.Pair.New()
enables type inference for this. If at some point you need a third item, don't doPair<A,Pair<B,C>>
; that way lies madness. That's a good time to make a real type. -
If computing some value repeatedly is expensive, don't roll your own caching. Use
Lazy<>
, and use theLazy.New()
wrapper to enable type inference. -
If you want caching which can be invalidated, use
Cached<>
, and use theCached.New()
wrapper to enable type inference there too. -
If you want a lazy key-value mapping, don't roll your own on
Dictionary<>
; useCache<>
, andCache.New()
for the type inference. -
Avoid nested types. They are poorly supported by various tools (VS included), and cannot be made less verbose by
using
. A partial exception to this is an enum of values to be used only by the enclosing class, but this is usually poor design.
-
Convert
break/continue
toreturn
by extracting a function. -
Favor standard query operators (in
System.Linq
) over hand-rolled loops whenever possible. -
The
forever
loop is writtenfor(;;)
, notwhile(true)
. -
Iterator blocks for infinite sequences are good. They can be easily composed with a filter such as
.Take(n)
. -
Iterator blocks which may take an arbitrarily long time to produce an element are very bad. For example, this simple iterator for infinitely repeating a sequence is poorly-behaved, since it hangs forever if
ts
is empty:
IEnumerable<T> Cycle<T>(this IEnumerable<T> ts)
{
for(;;)
foreach(var t in ts)
yield return t;
}
- Do not use XML comments. If you feel something is not obvious and needs comments, use the regular // comments.
TODO: Talk about a lot more things.