- Don't fight the community
- Don't fight the tooling
- Don't fight teammates
- Existing code style stands (until tooling cleans it up or a genuine code review occurs)
- Why have a style?
- Who uses the style?
- Any team may adopt this style.
- What is scope of the document?
- Style only?
- C# only? (what about F#, XAML)
- Smell avoidance?
- How is the style defined?
- When possible, the style settings files should be checked into source control
- Visual Studio (corefx.vssettings)
- Resharper "Solution team-shared" (panther.sln.DotSettings)
- EditorConfig (.editorconfig)
- Is the code the standard? Or are the style settings files?
- How is the style maintained?
- This is pretty organic
- How is the style enforced?
- Each team decides
using System;
using System.Collections;
using System.Collections.Generic;
using Another.Library;
Using See.How.These.Are.Sorted.Alphabetically;
using Some.Other.Library;
namespace Xample.CSharpCodingStyle
{
// 1b, 3
using ArgumentPairs = Dictionary<string,string>;
- (a) Namespaces imports at the top, unless it makes more sense to do otherwise .
- (b) Faux-typedef aliases are more succinct when put inside the namespace block.
- As in
using ArgumentPairs = Dictionary<string,string>;
- As in
- (c) Namespaces should be ordered alphabetically, except...
- (d) System.* namespaces should be grouped at the top.
- (a) In C#, when scope modifiers are ommitted, the type/member has the least-visible scope possible.
- (b) Specifying a scope modifier is necessary when extending scope, but is optional in the standard because it decreases the signal/noise ratio.
- (c)
static
comes after scope modifiers- Do:
public static
- Don't:
static public
- Do:
- (a) Spaces in types are undesireable since there would also be spaces separating it from the identifier.
- (b) Use
string[]
instead ofstring []
, andDictionary<string,string>
instead ofDictionary<string, string>
.
- (a) A single space around binary operators like
=
,<
,>
,+=
, etc. - (b) Prefer no spaces at the end of the line. (Avoid trailing whitespace)
- (c) No spaces before commas (except indent, of course). 1 space after commas unless at end of line.
- (d) [No space|single space] after keywords like if/else/switch/with/while/for/foreach.
delegate string Format(object o);
public enum BinaryDigit
{
Zero,
One,
}
readonly Arguments arguments;
public static readonly BinaryDigit DefaultDigit = BinaryDigit.Zero;
static void Main(string[] args)
{...}
[Test]
static void WhenItRains_ItPours()
{...}
- (a) Public, protected, internal, or anything usable outside the type (constants, fields, functions, etc.) use PascalCase.
- (b) Types, Delegates, Methods, Enums, constant fields are PascalCase.
- (c) Private fields use camelCase.
- (d) Variables and let bindings are camelCase.
- (e) Sometimes it seems necessary to put "punctuation" in a plain-language name, especially test names. Underscore works well for this:
public class AfterPrinting_StatusChangeDialog{...}
-
(a) When to comment
- Ottinger's Rules for Commenting Code
- Primary Rule - "Comments are for things that cannot be expressed in code."
- Redundancy Rule - "Comments which restate code must be deleted."
- Single Truth Rule - "If the comment says what the code could say, then the code must change to make the comment redundant."
- Ottinger's Rules for Commenting Code
-
(b)How to comment
- (b1) Use
//
for comments, unless only commenting part of a expression or line. - (b2) Comments should be very close to the code they comment.
- (b1) Use
"A common fallacy is to assume authors of incomprehensible code will somehow be able to express themselves lucidly and clearly in comments." - https://twitter.com/KevlinHenney/status/381021802941906944
/// <summary>
/// Implementation of for the FizzBuzz sequence.
/// </summary>
public static class FizzBuzz{...}
- (a) Use them for public classes, members, and arguments because our tooling checks for that.
- (a) Use spaces, not tabs
- (b) Use (N) spaces per indent. (Jeremy likes 2. Softek historically uses 3. VS Default is 4, so perhaps we use N=4.)
- (c) Do[n't] indent switch/case labels
- (d) Don't fight the tooling because it is frustrating and leads to larger diffs.
- Start trying to break lines before 80 characters unless it severaly hurts readability. 90 should probably be a max.
- (a) Generic type constraints
where TKey : IInterface, new()
should be on one line per type. TKey and TValue would each have a line. - (b) The
where
is indented one block further than the declaration.
- (a) The contents of each block is indented
- (b) Blocks that are one line long don't need surrounding {}.
- (c) Blocks that are one expression long don't require a surrounding {}.
- (d) Some feel that statements should or should not be balanced. Like in the PrintImperative example, don't fight the tooling.
- (e) Some feel that statements should or should not be balanced. Don't "correct" code already checked in because it makes the diff larger.
- (a) When specifying types that with language syntax like
string
,float
, prefer using keywords to the System.String type name. - (b) Often the compiler can infer the type of a binding assignment, like
var ageMonths = 12;
.- When writing new code, prefer the use of
var
. - When modifying existing code, adopt the existing style.
- When writing new code, prefer the use of
As the C# language becomes more expression-oriented (LINQ, expression-bodied members), this will probably become more of an issue.
- (a) When an expression is all that is necessary, prefer using an expression to a series of statements.
- (a) Optimize for testability, correctness, readability, cleanliness (SOLID principals).
- (b) Only sacrifice the above for performance if
- there is an established set of benchmarks and performance acceptance criteria
- and/or half the team agrees it's worth the sacrifice.
- AND the performance test suite is automated.
- Premature optimization often has a code smell
- (a) So you can Ctrl+End
- (b) Cleaner diffs
- (c) Don't fight the tooling
[STAThread]
static void Main()
{...}
// [Test, Pairwise]
[Test]
[Pairwise]
public void MyTest(
[Values("a", "b", "c")] string a,
[Values("+", "-")] string b,
[Values("x", "y")] string c)
{
Console.WriteLine("{0} {1} {2}", a, b, c);
}
- (a) Type/Member attributes usually go on their own line indented at the same level as the declaration.
- (b) When using parameter attributes, the attribute goes on the same line as the type and identifier.
public enum BinaryDigit
{
Zero,
One,
}
public static readonly BinaryDigit[] Five =
{
BinaryDigit.One,
BinaryDigit.Zero,
BinaryDigit.One,
};
public static readonly List<BinaryDigit> Six = new List<BinaryDigit>
{
BinaryDigit.One,
BinaryDigit.One,
BinaryDigit.Zero,
};
- (a) In enum declarations and initializers it is often useful to put each item on its own line. C# makes the trailing comma optional. **This guideline is all about reducing diff & merge conflicts.
- For new code, prefer a trailing comma when allowed, in case another item is added later.
- For existing code, only change it if there's another change to the line (like indention).
-
Guiding Principals
- Microsoft has blessed us with these features, let's use them
- New Code: Use them
- Existing Code: Use [Sparingly|Whole Hog|Ctrl_K,D ASAP|Minimize Diff]
-
- Initializers for auto-properties
- Getter-only auto-properties
- Expression-bodied function members
- Using static
- Use [sparingly|tastefully]
- Null-conditional operators
- String interpolation
- nameof expressions
- Index initializers
- Exception filters
- Extension Add methods in collection initializers
- Improved overload resolution
In this example, the parameter could be List<T>
, IList<T>
, IReadOnlyCollection<T>
,
and if you later remove the sill print statement at the front, IEnumerable<T>
.
static void PrintList(List<object> items)
{
Console.WriteLine($"Here are the {items.Count} items:");
foreach(var item in items)
PrintImperative(item);
}
One could argue that a method named Print*
doesn't need access
to the List<T>.Add
method, for example. So List<T>
and IList<T>
aren't
good choices. IEnumerable<T>
and IReadOnlyCollection<T>
then are better choices.
Resharper can suggest using the least-capable interface, like with a hint or suggestion.
Should the style specify anything about using base interfaces?
http://stackoverflow.com/questions/2164170/should-i-seal-all-classes-i-know-shouldnt-ever-be-used-as-a-base-class Eric Lippert's answer. Tl;Dr:
Seal the class. Unseal it later if you find that (1) was the right choice.