本风格指南适用于 Google 内部开发的 C# 代码,是 Google C# 代码的默认风格。 它在风格选择上与其他 Google 语言保持一致,例如 Google C++ 风格和 Google Java 风格。
命名规则遵循 Microsoft 的 C# 命名指南。 对于 Microsoft 未指定的命名指南(例如,私有变量和局部变量),规则取自 CoreFX C# 编码指南
规则总结
PascalCase。camelCase。_camelCase。MyRpc 而不是 MyRPCI 开头,例如 IInterface。PascalCase,例如 MyFile.cs。MyClass.cs。public protected internal private new abstract virtual override sealed static readonly extern unsafe volatile async。using 声明位于顶部,在任何命名空间之前。 using 导入顺序是字母顺序,除了 System 导入始终排在第一位。基于 Google Java 风格开发。
else 之间没有换行。if/for/while 等之后以及逗号之后有空格。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 的变量和字段应始终设置为 const。const 不可行,则 readonly 可以作为合适的替代方案。IReadOnlyCollection / IReadOnlyList / IEnumerable 用作方法的输入。IList 而不是 IEnumerable。 如果不转移所有权,则首选最具限制性的选项。ToList() 直接转换为容器的生成器代码的性能将低于直接填充容器的性能。=>)。{ get; set; } 语法。例如
int SomeProperty => _someProperty
结构与类截然不同
transform.position.x = 10 不会将变换的 position.x 设置为 10;此处的 position 是一个属性,该属性按值返回 Vector3,因此这只会设置原始副本的 x 参数。几乎总是使用类。
当类型可以像其他值类型一样处理时,请考虑使用结构 - 例如,如果类型的实例很小且通常生命周期很短,或者通常嵌入在其他对象中。 好的示例包括 Vector3、Quaternion 和 Bounds。
请注意,此指导可能因团队而异,例如,性能问题可能会迫使使用结构。
out。out 参数放在方法定义中所有其他参数之后。ref 应很少使用,仅在必须修改输入时使用。ref 作为传递结构的优化。ref 将可修改的容器传递到方法中。 仅当需要用完全不同的容器实例替换提供的容器时,才需要 ref。myList.Where(x) 而不是 myList where x。Container.ForEach(...)。List<> 而不是数组(请记住上面关于 IList / IEnumerable / IReadOnlyList 的指导)。List<>。List<> 都代表线性、连续的容器。std::vector 类似,数组的容量是固定的,而可以向 List<> 中添加内容。List<> 更灵活。Tuple<>,尤其是在返回复杂类型时。String.Format() vs String.Concat vs operator+operator+ 连接会降低速度并导致严重的内存抖动。StringBuilder 对于多个字符串连接会更快。usingusing 为长类型名称创建别名。 通常,这表明需要将 Tuple<> 转换为类。using RecordList = List<Tuple<int, float>> 可能应该是一个命名的类。using 语句仅在文件范围内,因此用处有限。 类型别名将不适用于外部用户。例如
var x = new SomeClass {
Property1 = value1,
Property2 = value2,
};
unity_app,命名空间不是必须的。out 值。备注
StatusOr。C#(像许多其他语言一样)没有提供一种明显的机制来在迭代时从容器中移除元素。 有几种选择
someList.RemoveAll(somePredicate)。RemoveAll 可能不足以满足需求。 一种常见的替代模式是在循环外部创建一个新容器,将要保留的元素插入到新容器中,并在迭代结束时将原始容器与新容器交换。Invoke() 并使用空条件运算符 - 例如 SomeDelegate?.Invoke()。 这在调用点清楚地标记了“正在调用一个委托”。 空检查简洁且对线程竞争条件具有鲁棒性。var 关键字var。鼓励
var apple = new Apple();,或 var request = Factory.Create<HttpRequest>();var item = GetItem(); ProcessItem(item);不鼓励
var success = true;var number = 12 * ReturnsFloat();var listOfItems = GetList();源自 Google C++ 风格指南。
当函数参数的含义不明显时,请考虑以下补救措施之一
enum 参数替换 bool 参数。 这将使参数值具有自描述性。考虑以下示例
// Bad - what are these arguments?
DecimalNumber product = CalculateProduct(values, 7, false, null);
与
// Good
ProductOptions options = new ProductOptions();
options.PrecisionDecimals = 7;
options.UseCache = CacheUsage.DontUseCache;
DecimalNumber product = CalculateProduct(values, options, completionDelegate: null);