-
Notifications
You must be signed in to change notification settings - Fork 0
Coding Standard
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, and that document [msdn:...] should be consulted where guidelines are missing here.
-
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;
}
TODO: Talk about a lot more things.