C# 风格指南
对于每个项目而言, 拥有定义良好且一致的编码约定非常重要,Godot也不例外.
本页面包含一份编码风格指南,Godot 本身的开发人员和贡献者都遵循该指南。因此,它的目标读者是希望为该项目做出贡献的人员,但是由于本文中提到的约定和规范被该语言用户最广泛采用,所以我们建议你也这样做,尤其是如果你还没有这样的指南。
备注
本文绝不是关于如何遵循标准编码约定或最佳实践的详尽指南。如果你不确定此处未涉及的方面,请参阅更全面的文档,例如 C# 编码约定或框架设计规范。
语言规范
Godot currently uses C# version 10.0 in its engine and example source code, as this is the version supported by .NET 6.0 (the current baseline requirement). So, before we move to a newer version, care must be taken to avoid mixing language features only available in C# 11.0 or later.
有关不同版本的C#功能的详细信息, 请参阅 C #中的新功能 .
格式
总体规范
使用换行符( LF )来换行, 而不是
CRLF
或CR
.在每个文件末尾使用一个换行符, 但 csproj 文件除外.
使用不带 字节顺序标记(BOM) 的 UTF-8 编码.
使用 4空格 代替制表符进行缩进(称为 “软制表符”).
如果长度超过100个字符, 请考虑将其分成几行.
换行符和空白行
对于一般缩进规则, 请遵循 Allman 风格, 它建议将与控制语句关联的大括号放在下一行, 缩进到同一级别:
// Use this style:
if (x > 0)
{
DoSomething();
}
// NOT this:
if (x > 0) {
DoSomething();
}
但是, 你可以选择省略括号内的换行符:
对于简单的属性访问者.
对于简单对象, 数组, 或集合初始化.
对于抽象的自动属性, 索引器, 或事件声明.
// You may put the brackets in a single line in following cases:
public interface MyInterface
{
int MyProperty { get; set; }
}
public class MyClass : ParentClass
{
public int Value
{
get { return 0; }
set
{
ArrayValue = new [] {value};
}
}
}
插入一个空行:
在一列
using
语句之后.在方法, 属性, 和内部类型声明之间.
在每个文件的末尾.
字段声明和常量声明可以根据相关性编组在一起. 在这种情况下, 请考虑在编组之间插入空白行以便于阅读.
避免插入空白行:
在开括号
{
之后。在闭合括号
}
之前。在注释块或单行注释之后.
与另一个空白行相邻.
using System;
using Godot;
// Blank line after `using` list.
public class MyClass
{ // No blank line after `{`.
public enum MyEnum
{
Value,
AnotherValue // No blank line before `}`.
}
// Blank line around inner types.
public const int SomeConstant = 1;
public const int AnotherConstant = 2;
private Vector3 _x; // Related constants or fields can be
private Vector3 _y; // grouped together.
private float _width;
private float _height;
public int MyProperty { get; set; }
// Blank line around properties.
public void MyMethod()
{
// Some comment.
AnotherMethod(); // No blank line after a comment.
}
// Blank line around methods.
public void AnotherMethod()
{
}
}
使用空格
插入一个空格:
在二元和三元运算符的两侧。
在左括号和
if
、for
、foreach
、catch
、while
、lock
、using
关键字之间。在单行访问器块之前和之内.
在单行访问器块中的访问器之间.
在不是在行尾的逗号之后.
在
for
语句中的分号之后.在单行
case
语句中的冒号之后.在类型声明中冒号的两侧。
在 lambda 箭头的两侧。
在单行注释符号(
//
)之后,并且如果在行末使用,则在它之前。After the opening brace, and before the closing brace in a single line initializer.
不要使用空格:
- 在类型转换括号后.
下面的示例根据上述的一些约定显示了对空格的正确使用:
public class MyClass<A, B> : Parent<A, B>
{
public float MyProperty { get; set; }
public float AnotherProperty
{
get { return MyProperty; }
}
public void MyMethod()
{
int[] values = { 1, 2, 3, 4 };
int sum = 0;
// Single line comment.
for (int i = 0; i < values.Length; i++)
{
switch (i)
{
case 3: return;
default:
sum += i > 2 ? 0 : 1;
break;
}
}
i += (int)MyProperty; // No space after a type cast.
}
}
命名规定
对所有命名空间、类型名称、成员级别标识符(即方法、属性、常量、事件)使用 PascalCase,私有字段除外:
namespace ExampleProject
{
public class PlayerCharacter
{
public const float DefaultSpeed = 10f;
public float CurrentSpeed { get; set; }
protected int HitPoints;
private void CalculateWeaponDamage()
{
}
}
}
将 camelCase 用于所有其他标识符(即局部变量、方法参数),并使用下划线(_
)作为私有字段的前缀(但不能用于方法或属性,如上所述):
private Vector3 _aimingAt; // Use a `_` prefix for private fields.
private void Attack(float attackStrength)
{
Enemy targetFound = FindTarget(_aimingAt);
targetFound?.Hit(attackStrength);
}
类似 UI
这种只有两个字母的首字母缩写应特殊处理,使用 PascalCase 时都应写作大写字母,否则都应写作小写字母。
请注意,id
不是首字母缩写,因此应将其视为普通标识符:
public string Id { get; }
public UIManager UI
{
get { return uiManager; }
}
通常不建议将类型名称用作标识符的前缀,例如 string strText
或 float fPower
。但是,对于接口来说是个例外,实际上,接口应该在其名称前加上大写字母 I
,例如 IInventoryHolder
或 IDamageable
。
最后,请考虑有意义的名称,请勿对名称进行过度缩写,以免影响可读性。
例如,如果你想编写代码来查找附近的敌人并用武器击中它,请选择:
FindNearbyEnemy()?.Damage(weaponDamage);
而不是:
FindNode()?.Change(wpnDmg);
成员变量
如果变量只在方法中使用,请勿将该变量声明为成员变量,因为难以定位在何处使用了该变量。相反,你应该将这些变量在方法内部定义为局部变量。
局部变量
局部变量的声明位置离首次使用该局部变量的位置越近越好,让人更容易跟上代码的思路,而不需要上翻下找该变量的声明位置。
隐式类型的局部变量
考虑使用隐式类型化(var
)声明局部变量,但是请只在赋值右侧能够推出该类型时使用:
// You can use `var` for these cases:
var direction = new Vector2(1, 0);
var value = (int)speed;
var text = "Some value";
for (var i = 0; i < 10; i++)
{
}
// But not for these:
var value = GetValue();
var velocity = direction * 1.5;
// It's generally a better idea to use explicit typing for numeric values, especially with
// the existence of the `real_t` alias in Godot, which can either be double or float
// depending on the build configuration.
var value = 1.5;
其他注意事项
使用显式访问修饰符。
使用属性而不是非私有字段。
按此顺序使用修饰符:
public
/protected
/private
/internal
/virtual
/override
/abstract
/new
/static
/readonly
。避免在不必要时,为成员使用完全限定的名称或
this.
前缀。删除未使用的
using
语句和不必要的括号。考虑省略类型的默认初始值。
考虑使用空条件运算符或类型初始化器来使代码更紧凑。
当值可能会成为另一种不同的类型,请使用安全类型转换,否则使用直接类型转换。