跳到主要内容

编码规范

Version 2.4.4 by win for Unity

全局修饰符顺序

在本规范下,修饰符优先级从高到低排序为:

  1. publicinternalprotected internalprotectedprivate protectedprivate

  2. staticnewsealedoverrideconst

  3. readonlyeventasync

优先级越高,则书写时越应位于左侧。

以下代码被视为正确
public static readonly int number;
以下代码被视为错误
static public readonly int number;

全局标识符约定

在本规范下,所有遵守此约定的标识符应使用英语书写,允许使用缩写,不允许简写。若某个单词作为缩写(如UI,ID,AI,GUID)使用,那么该单词必须全部大写或全部小写。

注意

尽管本规范没有规定任何可使用的缩写单词,但当开发人员使用缩写时,该缩写单词必须是普遍使用并被认可的,如将 Callback 缩写为 CB 是错误的,而将 Artificial intelligence 缩写为 AI 则是正确的。


字段/属性约定

标识符约定

在本规范下,字段和属性的标识符使用小驼峰法 (lowerCamelCase) 命名,在此基础上,有下列特殊约定:

Boolean 类型约定

所有 bool 类型的字段/属性严禁以 is 为前缀。

错误的布尔类型字段
public bool isOpen;
正确的布尔类型字段
public bool opened;

函数指针(委托和事件)约定

所有函数指针类型的字段/属性必须以单词 on 为前缀。

正确的函数指针字段
public event Action onOpen;

数据结构约定

数据结构类型的字段/属性有以下约定:

  • 数组以单词 Array 或复数字母 s 作为后缀;
  • 字典以单词 Dictionary 作为后缀;
  • 列表以单词 List 作为后缀;

其余数据结构或者自定义数据结构,均以其类名作为后缀;

接口类型约定

如果类型显式被指定为接口,则标识符可以省略前缀 I

正确的接口类型标识符
public IData data;
public IData data2 { get; private set; }

属性操作数约定

如果某一字段只是属性的操作数,并且无其它任何含义,那么该字段以下划线作为前缀,且名称与属性名一致,如:

正确的操作数标识符
public float time
{
get => _time;
set { if (value > 0) _time = value; }
}
private float _time;

属性使用约定

对于所有可改写成Lambda表达式主体的访问器,必须改写; 严禁使用只对一个变量进行只读只写操作的属性。

以下代码被视为错误
public float time { get { return _time; } set { _time = value; } } 
private float _time;

以上代码必须改为自动属性:

自动属性
public float time { get; set; }

但如果在属性中对字段进行操作,那么以下代码是允许的:

在属性中操作字段
public float time
{
get => _time;
set { if (value > 0) _time = value; }
}
private float _time;

字段使用约定

不允许任何形式的多字段声明,以下代码是不允许的:

不允许多字段声明
public float a, b, c;

类(class)约定

标识符约定

类标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名,且有以下特殊约定:

  • 如果类的主要功能是提供对某个类型的扩展,则类名应为 <扩展对象的类名>Extension (如 TransformExtension);
  • 如果类的主要功能是提供工具函数且为 static,则类名应为 <功能>Utility (如 DataUtility);

修饰符约定

  • 密封约定:若类不进行任何派生,则必须加上 sealed 修饰符。
  • 访问范围约定:若类不对外公开,则必须使用 internal 访问修饰符。

特性(Attribute)约定

特性标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名,并以Attribute作为后缀:

特性应用约定

在本规范下,特性只可以置于声明实体的上方,当有多个特性时,必须放置在同一个方括号内。

错误的特性应用
public class Data
{
[System.NonSerialized] public string name;
}
public class Data
{
[UnityEngine.Space]
[System.NonSerialized]
public string name;
}
正确的特性应用
public class Data
{
[System.NonSerialized]
public string name;
}
public class Data
{
[System.NonSerialized, UnityEngine.Space]
public string name;
}

结构(struct)约定

标识符约定

结构标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名。


接口(interface)约定

标识符约定

接口标识符在遵守 全局标识符约定 的情况下使用微软规范命名: 即先以大驼峰法 (‌Upper Camel Case) 命名,再使用字母 I 作为标识符前缀。


枚举(enum)约定

标识符约定

枚举标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名。

枚举成员标识符约定

枚举成员标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名。

枚举成员定义约定

在本规范下,枚举成员必须按列定义:

错误的枚举成员定义
public enum DirectionType
{
Up, Left, Right, Bottom
}
正确的枚举成员定义
public enum DirectionType
{
Up,
Left,
Right,
Bottom
}

预处理器约定

标识符约定

预处理器符号标识符应使用大写英语书写,允许使用缩写,不允许简写,单词之间必须使用下划线。

正确的预处理器标识符

#if PLATFORM_WINDOWS


泛型约定

标识符约定

泛型标识符仅使用单个大写的英文字母,且第一个标识符总是使用大写字母 T,之后的标识符可使用除 T 之外的其它大写字母。


方法约定

标识符约定

方法标识符在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名,在此基础上,有以下特殊情况:

回调方法

如果方法仅作为回调方法使用,那么其标识符必须以单词 Callback 作为后缀。

回调方法命名示例
public void MouseDownCallback();

异步方法

如果方法为异步方法,且该方法有对应的非异步方法,那么其标识符必须以单词 Async 作为后缀:

异步方法命名示例
public void LoadResource();
public Task LoadResourceAsync();

特殊方法

如果方法满足以下任意条件,则允许在方法中加入下划线和简写来增加可读性:

  • 由代码生成的方法;
  • 有大量同名的前缀/后缀;

协程方法(Unity协程函数)

在Unity中的协程函数命名必须以后缀 Coroutine 结尾:

协程方法命名示例
public IEnumerator PlayCoroutine();

虚方法

如果方法作为虚方法并且没有任何语句,则需加上前缀 On

虚方法命名示例
public virtual OnUpdate();

形参标识符约定

形参均使用小驼峰法 (lowerCamelCase) 命名,在此基础上,有以下特殊情况:

  • 扩展方法的扩展形参:必须命名为 self
  • 显式/隐式转换方法:自身参数形参名必须是 self

变量标识符约定

变量使用小驼峰法 (lowerCamelCase) 命名,如无可读性需要,其名称应总是和其类型名一致。 对于类型被显式指定为接口的变量,其变量名可以省略前缀 I

变量命名示例
PlayerData playerData = GetPlayerData();
IData data = GetData();

若有可能需要辨识的情景,允许自定义变量名,如下例: 玩家和敌人都属于 Unit 的实例,此时可以为它们定义一个唯一标识符来区分:

变量辨识命名示例
Unit player = new Unit();
Unit enemy = new Unit();

Boolean 类型变量约定

所有 bool 类型的变量严禁以 is 为前缀。

错误的布尔类型变量
bool isOpen;
正确的布尔类型变量
bool opened;

标签标识符

标签在遵守 全局标识符约定 的情况下使用大驼峰法 (‌Upper Camel Case) 命名。


语句约定

using语句

对于非托管资源的释放必须使用 using 语句或手动释放,严禁使用 using 语句块释放资源。


代码块约定

在本规范下,代码块应该在逻辑无关但代码上下文被关联的情况下使用(如 swith 语句中的 case 块)。原则上不应在其它方式下使用代码块。


换行/新行约定

左大括号换行原则

在本规范下,左大括号换行。

空白行约定

在本规范下,代码结构使用单行分隔,不允许出现连续空白行。

语句换行原则

标签语句必须换行,以下代码第2行是不允许的:

以下代码被视为错误
int value = 0;
ContinueAdd: value++;
if (value < 100) goto ContinueAdd;
Debug.Log(value);

其它语句严禁换行,即使语句超出了可见范围。以下代码是不允许的:

以下代码被视为错误
int number = 1 + 2 + 3 +
4;

新行原则

在本规范下,如果代码块中的语句只有一条,则不另起新行,如以下代码是不允许的:

以下代码被视为错误
if (number < 0)
{
Debug.Log("Error");
}

if (number < 0)
Debug.Log("Error");
以下代码被视为正确
if (number < 0) Debug.Log("Error");

whileforforeach 等流程控制语句和 try-catch-finally 异常处理语句同理。 case 子句同理,此时 breakreturn 应与 case 子句在同一行,如:

case子句换行示例
swtich (tokenType)
{
case TokenType.Int: break;
case TokenType.String: break;
}

书写优先级

本规范下的书写优先级指代码结构在文件中所在的行数。优先级越高,结构所在的行数越小,越靠上。

总优先级修饰符优先级方法优先级
using指令const静态构造函数
别名定义static readonly静态方法
using static指令static构造函数
属性event实例方法
字段readonly抽象方法
索引器虚方法
方法覆写方法
接口实现
显式/隐式转换
运算符重载
内部类型定义

文件与定义

本规范下,除了内部类型定义,一个文件内只允许定义一个类型。

以下定义被视为错误
Item.cs
public class Item
{
public ItemType itemType;
}

public enum ItemType
{
Weapon,
Armor
}

ItemType 应该新建一个文件定义,structenuminterfacedelegate 等类型同理。

访问修饰符约定

在声明和定义任何类型、字段、属性、方法时,必须 显式声明 访问修饰符。


字面量约定

在本规范下,float 类型的字面量必须跟后缀名 f

在本规范下,所有浮点数类型的字面量禁止省略 0

以下浮点数定义被视为错误
float number = .5f;

匿名方法和Lambda表达式约定

在本规范下,禁止在命名方法中使用Lambda主体定义。

以下定义被视为错误
public int Add(int a, int b) => a + b;

本规范禁止使用匿名方法,所有匿名方法必须替换成等价的Lambda表达式,且有以下规定:

参数列表

如果参数列表中只有一个形参,则不使用括号。

以下代码被视为错误
(message) => Debug.Log(message);
以下代码被视为正确
message => Debug.Log(message);

主体

如果方法只有一句语句,则不使用语句块。

以下代码被视为错误
message => { Debug.Log(message); };
以下代码被视为正确
 message => Debug.Log(message);