什么是编码规范
编码规范指的是从代码的正确性、稳定性、可读性等方面识别出来的在编码过程中要遵循的规则,例如代码的排版规则,注释方法,函数、变量命名规则等。
大厂几乎都有自己的语言编码规范,并且要求程序员编码时严格执行编码规范。
为何要有编码规范
提高代码的可读性
如果没有代码规范的规约,即便是同一个人,他在不同时期代码的写法与风格都会有差异,更不用说由多人组成的团队了。相同的命名规则,编排方法,注释原则,能大大提高程序的可读性。
提高代码的正确性和健壮性
编码规范一般都会有基于准确性和安全性方面的条例,帮助程序员编写出准确性高和健壮性好的代码。
快速提高程序员的编码能力
编码规范往往都是行业内人士的经验积累,很多条例都是由过来人踩过的坑,吃过的亏转换而来。对于经验较少的程序员,可以通过阅读和遵守编码规范快速提升自身的编码能力,写出更加高质量的代码。
总结:任何一个傻瓜都能写出计算机可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员
代码结构
缩进
每个缩进级别使用 4 个空格。
制表符或空格
空格是首选的缩进方法。 应该避免混合使用制表符和空格。
空行
在 solidity 源码中合约声明之间留出两个空行。
正确写法:
contract A {
...
}
contract B {
...
}
contract C {
...
}
错误写法:
contract A {
...
}
contract B {
...
}
contract C {
...
}
在一个合约中的函数声明之间留有一个空行。在相关联的各组单行语句之间可以省略空行。
正确写法:
contract A {
function spam() public;
function ham() public;
}
contract B is A {
function spam() public {
...
}
function ham() public {
...
}
}
错误写法:
contract A {
function spam() public {
...
}
function ham() public {
...
}
}
源文件编码格式
首选 UTF-8 或 ASCII 编码。
Imports 规范
Import 语句应始终放在文件的顶部。
正确写法:
import "owned";
contract A {
...
}
contract B is owned {
...
}
错误写法:
contract A {
...
}
import "owned";
contract B is owned {
...
}
函数顺序
排序有助于读者识别他们可以调用哪些函数,并更容易地找到构造函数和 fallback 函数的定义。
函数应根据其可见性和顺序进行分组:
- 构造函数
- fallback 函数(如果存在)
- 外部函数
- 公共函数
- 内部函数和变量
- 私有函数和变量
在一个分组中,把 constant
函数放在最后。
正确写法:
contract A {
function A() public {
...
}
function() public {
...
}
// External functions
// ...
// External functions that are constant
// ...
// Public functions
// ...
// Internal functions
// ...
// Private functions
// ...
}
错误写法:
contract A {
// External functions
// ...
// Private functions
// ...
// Public functions
// ...
function A() public {
...
}
function() public {
...
}
// Internal functions
// ...
}
表达式中的空格
在以下情况下避免无关的空格:
除单行函数声明外,紧接着小括号,中括号或者大括号的内容应该避免使用空格。
正确写法:
spam(ham[1], Coin({name: "ham"}));
错误写法:
spam( ham[ 1 ], Coin( { name: "ham" } ) );
除外:
function singleLine() public { spam(); }
紧接在逗号,分号之前:
正确写法:
function spam(uint i, Coin coin) public;
错误写法:
function spam(uint i , Coin coin) public ;
赋值或其他操作符两边多于一个的空格:
正确写法:
x = 1;
y = 2;
long_variable = 3;
错误写法:
x = 1;
y = 2;
long_variable = 3;
fallback 函数中不要包含空格:
正确写法:
function() public {
...
}
控制结构
用大括号表示一个合约,库、函数和结构。 应该:
- 开括号与声明应在同一行。
- 闭括号在与之前函数声明对应的开括号保持同一缩进级别上另起一行。
- 开括号前应该有一个空格。
正确写法:
contract Coin {
struct Bank {
address owner;
uint balance;
}
}
错误写法:
contract Coin
{
struct Bank {
address owner;
uint balance;
}
}
对于控制结构 if
, else
, while
, for
的实施建议与以上相同。
另外,诸如 if
, else
, while
, for
这类的控制结构和条件表达式的块之间应该有一个单独的空格, 同样的,条件表达式的块和开括号之间也应该有一个空格。
正确写法:
if (...) {
...
}
for (...) {
...
}
错误写法:
if (...)
{
...
}
while(...){
}
for (...) {
...;}
对于控制结构, 如果 其主体内容只包含一行,则可以省略括号。
正确写法:
if (x < 10)
x += 1;
错误写法:
if (x < 10)
someArray.push(Coin({
name: 'spam',
value: 42
}));
对于具有 else 或 else if 子句的 if 块, else 应该是与 if 的闭大括号放在同一行上。 这一规则区别于 其他块状结构。
正确写法:
if (x < 3) {
x += 1;
} else if (x > 7) {
x -= 1;
} else {
x = 5;
}
if (x < 3)
x += 1;
else
x -= 1;
错误写法:
if (x < 3) {
x += 1;
}
else {
x -= 1;
}
函数声明
对于简短的函数声明,建议函数体的开括号与函数声明保持在同一行。
闭大括号应该与函数声明的缩进级别相同。
开大括号之前应该有一个空格。
正确写法:
function increment(uint x) public pure returns (uint) {
return x + 1;
}
function increment(uint x) public pure onlyowner returns (uint) {
return x + 1;
}
错误写法:
function increment(uint x) public pure returns (uint)
{
return x + 1;
}
function increment(uint x) public pure returns (uint){
return x + 1;
}
function increment(uint x) public pure returns (uint) {
return x + 1;
}
function increment(uint x) public pure returns (uint) {
return x + 1;}
函数的可见性修饰符应该出现在任何自定义修饰符之前。
正确写法:
function kill() public onlyowner {
selfdestruct(owner);
}
错误写法:
function kill() onlyowner public {
selfdestruct(owner);
}
对于长函数声明,建议将每个参数独立一行并与函数体保持相同的缩进级别。闭括号和开括号也应该独立一行并保持与函数声明相同的缩进级别。
正确写法:
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f
)
public
{
doSomething();
}
错误写法:
function thisFunctionHasLotsOfArguments(address a, address b, address c,
address d, address e, address f) public {
doSomething();
}
function thisFunctionHasLotsOfArguments(address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}
如果一个长函数声明有修饰符,那么每个修饰符应该下沉到独立的一行。
正确写法:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(
address x,
address y,
address z,
)
public
onlyowner
priced
returns (address)
{
doSomething();
}
错误写法:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address) {
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public onlyowner priced returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address) {
doSomething();
}
对于继承合约中需要参数的构造函数,如果函数声明很长或难以阅读,建议将基础构造函数像多个修饰符的风格那样 每个下沉到一个新行上书写。
正确写法:
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
public
{
// do something with param5
}
}
错误写法:
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
public
{
// do something with param5
}
}
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
public {
// do something with param5
}
}
当用单个语句声明简短函数时,允许在一行中完成。
允许:
function shortFunction() public { doSomething(); }
这些函数声明的准则旨在提高可读性。 因为本指南不会涵盖所有内容,作者应该自行作出最佳判断。
变量声明
数组变量的声明在变量类型和括号之间不应该有空格。
正确写法:
uint[] x;
错误写法:
uint [] x;
其他建议
-
字符串应该用双引号而不是单引号
正确写法:
str = "foo"; str = "Hamlet says, 'To be or not to be...'";
错误写法:
str = 'bar'; str = '"Be yourself; everyone else is already taken." -Oscar Wilde';
-
操作符两边应该各有一个空格
正确写法:
x = 3; x = 100 / 10; x += 3 + 4; x |= y && z;
错误写法:
x=3; x = 100/10; x += 3+4; x |= y&&z;
-
为了表示优先级,高优先级操作符两边可以省略空格。这样可以提高复杂语句的可读性。你应该在操作符两边总是使用相同的空格数:
正确写法:
x = 2**3 + 5; x = 2*y + 3*z; x = (a+b) * (a-b);
错误写法:
x = 2** 3 + 5; x = y+z; x +=1;
命名规范
当完全采纳和使用命名规范时会产生强大的作用。 当使用不同的规范时,则不会立即获取代码中传达的重要元信息。这里给出的命名建议旨在提高可读性,因此它们不是规则,而是透过名称来尝试和帮助传达最多的信息。
命名方式
为了避免混淆,下面的名字用来指明不同的命名方式。
b
(单个小写字母)B
(单个大写字母)lowercase
(小写)lower_case_with_underscores
(小写和下划线)UPPERCASE
(大写)UPPER_CASE_WITH_UNDERSCORES
(大写和下划线)CapitalizedWords
(驼峰式,首字母大写)mixedCase
(混合式,与驼峰式的区别在于首字母小写!)Capitalized_Words_With_Underscores
(首字母大写和下划线)
注意:当在驼峰式命名中使用缩写时,应该将缩写中的所有字母都大写。 因此 HTTPServerError 比 HttpServerError 好。当在混合式命名中使用缩写时,除了第一个缩写中的字母小写(如果它是整个名称的开头的话)以外,其他缩写中的字母均大写。 因 xmlHTTPRequest 比 XMLHTTPRequest 更好。
应避免的名称
l
- el 的小写方式O
- oh 的大写方式I
- eye 的大写方式
切勿将任何这些用于单个字母的变量名称。 他们经常难以与数字 1 和 0 区分开。
合约和库名称
合约和库名称应该使用驼峰式风格。比如:SimpleToken
,SmartBank
,CertificateHashRepository
,Player
。
结构体名称
结构体名称应该使用驼峰式风格。比如:MyCoin
,Position
,PositionXY
。
事件名称
事件名称应该使用驼峰式风格。比如:Deposit
,Transfer
,Approval
,BeforeTransfer
,AfterTransfer
。
函数名称
函数名称不同于结构,应该使用混合式命名风格。比如:getBalance
,transfer
,verifyOwner
,addMember
,changeOwner
。
函数参数命名
函数参数命名应该使用混合式命名风格。比如:initialSupply
,account
,recipientAddress
,senderAddress
,newOwner
。 在编写操作自定义结构的库函数时,这个结构体应该作为函数的第一个参数,并且应该始终命名为 self
。
局部变量和状态变量名称
使用混合式命名风格。比如:totalSupply
,remainingSupply
,balancesOf
,creatorAddress
,isPreSale
,tokenExchangeRate
。
常量命名
常量应该全都使用大写字母书写,并用下划线分割单词。比如:MAX_BLOCKS
,TOKEN_NAME
,TOKEN_TICKER
,CONTRACT_VERSION
。
修饰符命名
使用混合式命名风格。比如:onlyBy
,onlyAfter
,onlyDuringThePreSale
。
枚举变量命名
在声明简单类型时,枚举应该使用驼峰式风格。比如:TokenGroup
,Frame
,HashStyle
,CharacterLocation
。
避免命名冲突
当所起名称与内建或保留关键字相冲突时,建议照此惯例在名称后边添加下划线。如 single_trailing_underscore_