This style guide is for C# code developed internally at Google, and is the default style for C# code at Google. It makes stylistic choices that conform to other languages at Google, such as Google C++ style and Google Java style.
String.Format() vs String.Concat vs operator+usingvar keywordNaming rules follow Microsoft’s C# naming guidelines. Where Microsoft’s naming guidelines are unspecified (e.g. private and local variables), rules are taken from the CoreFX C# coding guidelines
Rule summary:
PascalCase.camelCase._camelCase.MyRpc instead of MyRPCI, e.g. IInterface.PascalCase, e.g. MyFile.cs.MyClass.cs.public protected internal private
new abstract virtual override sealed static readonly extern unsafe volatile
async.using declarations go at the top, before any namespaces. using
import order is alphabetical, apart from System imports which always go
first.Developed from Google Java style.
else.if/for/while etc., and after commas.using System; // `using` goes at the top, outside the
// namespace.
namespace MyNamespace { // Namespaces are PascalCase.
// Indent after namespace.
public interface IMyInterface { // Interfaces start with 'I'
public int Calculate(float value, float exp); // Methods are PascalCase
// ...and space after comma.
}
public enum MyEnum { // Enumerations are PascalCase.
Yes, // Enumerators are PascalCase.
No,
}
public class MyClass { // Classes are PascalCase.
public int Foo = 0; // Public member variables are
// PascalCase.
public bool NoCounting = false; // Field initializers are encouraged.
private class Results {
public int NumNegativeResults = 0;
public int NumPositiveResults = 0;
}
private Results _results; // Private member variables are
// _camelCase.
public static int NumTimesCalled = 0;
private const int _bar = 100; // const does not affect naming
// convention.
private int[] _someTable = { // Container initializers use a 2
2, 3, 4, // space indent.
}
public MyClass() {
_results = new Results {
NumNegativeResults = 1, // Object initializers use a 2 space
NumPositiveResults = 1, // indent.
};
}
public int CalculateValue(int mulNumber) { // No line break before opening brace.
var resultValue = Foo * mulNumber; // Local variables are camelCase.
NumTimesCalled++;
Foo += _bar;
if (!NoCounting) { // No space after unary operator and
// space after 'if'.
if (resultValue < 0) { // Braces used even when optional and
// spaces around comparison operator.
_results.NumNegativeResults++;
} else if (resultValue > 0) { // No newline between brace and else.
_results.NumPositiveResults++;
}
}
return resultValue;
}
public void ExpressionBodies() {
// For simple lambdas, fit on one line if possible, no brackets or braces required.
Func<int, int> increment = x => x + 1;
// Closing brace aligns with first character on line that includes the opening brace.
Func<int, int, long> difference1 = (x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
};
// If defining after a continuation line break, indent the whole body.
Func<int, int, long> difference2 =
(x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
};
// Inline lambda arguments also follow these rules. Prefer a leading newline before
// groups of arguments if they include lambdas.
CallWithDelegate(
(x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
});
}
void DoNothing() {} // Empty blocks may be concise.
// If possible, wrap arguments by aligning newlines with the first argument.
void AVeryLongFunctionNameThatCausesLineWrappingProblems(int longArgumentName,
int p1, int p2) {}
// If aligning argument lines with the first argument doesn't fit, or is difficult to
// read, wrap all arguments on new lines with a 4 space indent.
void AnotherLongFunctionNameThatCausesLineWrappingProblems(
int longArgumentName, int longArgumentName2, int longArgumentName3) {}
void CallingLongFunctionName() {
int veryLongArgumentName = 1234;
int shortArg = 1;
// If possible, wrap arguments by aligning newlines with the first argument.
AnotherLongFunctionNameThatCausesLineWrappingProblems(shortArg, shortArg,
veryLongArgumentName);
// If aligning argument lines with the first argument doesn't fit, or is difficult to
// read, wrap all arguments on new lines with a 4 space indent.
AnotherLongFunctionNameThatCausesLineWrappingProblems(
veryLongArgumentName, veryLongArgumentName, veryLongArgumentName);
}
}
}
const should always be made const.const isn’t possible, readonly can be a suitable alternative.IReadOnlyCollection).
IEnumerable only if the implementation can iterate over the
collection at most once. This indicates that it is safe for the caller
to supply a generator.IList over List. If not
transferring ownership, prefer the most restrictive, as for inputs.ToList()
will be less performant than filling in a container directly.=>) when possible.{ get; set; } syntax.For example:
int SomeProperty => _someProperty
void SomeMethod() => throw new NotImplementedException();
Structs are very different from classes:
transform.position.x = 10 doesn’t set the transform’s
position.x to 10; position here is a property that returns a Vector3
by value, so this just sets the x parameter of a copy of the original.Almost always use a class.
Consider struct when the type can be treated like other value types - for example, if instances of the type are small and commonly short-lived or are commonly embedded in other objects. Good examples include Vector3, Quaternion and Bounds.
Note that this guidance may vary from team to team where, for example, performance issues might force the use of structs.
out for additional returns that are not also inputs.out parameters after all other parameters in the method definition.ref should be used rarely, when mutating an input is necessary.ref as an optimisation for passing structs.ref to pass a modifiable container into a method. ref is only
required when the supplied container needs be replaced with an entirely
different container instance.myList.Where(x) to myList where x.Container.ForEach(...) for anything longer than a single statement.List<> over arrays for public variables, properties,
and return types (keeping in mind the guidance on IList / IEnumerable /
IReadOnlyList above).List<> when the size of the container can change.List<> both represent linear, contiguous containers.std::vector, arrays are of fixed capacity,
whereas List<> can be added to.List<> is
more flexible.Tuple<>, particularly when
returning complex types.String.Format() vs String.Concat vs operator+operator+ concatenations will be slower and cause
significant memory churn.StringBuilder will be faster for multiple
string concatenations.usingusing. Often this is a sign
that a Tuple<> needs to be turned into a class.
using RecordList = List<Tuple<int, float>> should probably be a
named class instead.using statements are only file scoped and so of limited use.
Type aliases will not be available for external users.For example:
var x = new SomeClass {
Property1 = value1,
Property2 = value2,
};
unity_app, namespaces are not necessary.out value.Notes:
StatusOr equivalent in the future, if there is enough demand.C# (like many other languages) does not provide an obvious mechanism for removing items from containers while iterating. There are a couple of options:
someList.RemoveAll(somePredicate) is recommended.RemoveAll may not be
sufficient. A common alternative pattern is to create a new container
outside of the loop, insert items to keep in the new container, and swap the
original container with the new one at the end of iteration.Invoke() and use the null conditional
operator - e.g. SomeDelegate?.Invoke(). This clearly marks the call at the
callsite as ‘a delegate that is being called’. The null check is concise and
robust against threading race conditions.var keywordvar is encouraged if it aids readability by avoiding type names
that are noisy, obvious, or unimportant.Encouraged:
var apple = new Apple();, or var
request = Factory.Create<HttpRequest>();var item = GetItem(); ProcessItem(item);Discouraged:
var success = true;var
number = 12 * ReturnsFloat();var
listOfItems = GetList();Derived from the Google C++ style guide.
When the meaning of a method argument is nonobvious, consider one of the following remedies:
bool argument with an
enum argument. This will make the argument values self-describing.Consider the following example:
// What are these arguments?
DecimalNumber product = CalculateProduct(values, 7, false, null);
versus:
var options = new ProductOptions();
options.PrecisionDecimals = 7;
options.UseCache = CacheUsage.DontUseCache;
DecimalNumber product = CalculateProduct(values, options, completionDelegate: null);
(Derived from the Google C++ style guide.)
Comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next person who will need to understand your code. Be generous — the next one may be you!
Every non-obvious class should have an accompanying comment that describes what it is for and how it should be used.
The class comment should provide the reader with enough information to know how and when to use the class, as well as any additional considerations necessary to correctly use the class.
Almost every method should have comments immediately preceding it that describe what the method does and how to use it. These comments may be omitted only if the method is simple and obvious (e.g., simple accessors). Method comments should be written with an implied subject of This method and should start with the verb phrase; for example, “Opens the file”, rather than “Open the file”.
Types of things to mention in comments:
What the inputs and outputs are. If method argument names are provided in
backticks, then code-indexing tools may be able to present the
documentation better.
Whether the object remembers arguments beyond the duration of the method call.
Whether any of the arguments can be null.
If there are any performance implications of how a method is used.
However, do not be unnecessarily verbose or state the completely obvious.
When documenting method overrides, focus on the specifics of the override itself, rather than repeating the comment from the overridden method. In many of these cases, the override needs no additional documentation and thus no comment is required.
When commenting constructors, remember that the person reading your code knows what constructors are for, so comments that just say something like “creates this object” are not useful.
The purpose of each class data member must be clear. If there are any invariants (special values, relationships between members, lifetime requirements) not clearly expressed by the type and name, they must be commented. However, if the type and name suffice, no comment is needed.
In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.
Tricky or complicated code blocks should have comments before them.