云社区 博客 博客详情
云社区 博客 博客详情

《深入以太坊智能合约开发》 ——3.4 Solidity源代码书写风格

华章计算机 发表于 2020-02-18 20:52:28 02-18 20:52
华章计算机 发表于 2020-02-18 20:52:28 2020-02-18
0
0

【摘要】 本节书摘来自华章计算机《深入以太坊智能合约开发》 —— 书中第3章,第3.4节,作者是杨 镇 姜信宝 朱智胜 盖方宇 。

3.4 Solidity源代码书写风格

本节旨为读者提供Solidity代码书写的风格指南,约定Solidity代码的编码规范。然而源代码的写作规范是不断变化和演进的,旧的、过时的编码规范会被淘汰,而新的、有用的规范会被添加进来。Solidity代码书写的风格指南中的结构和许多建议是取自Python的PEP8 style guide。

风格指南是关于一致性的。重要的是与此风格指南保持一致,但项目中的一致性更重要。一个模块或功能内的一致性是重要的。但最重要的一点是:知道什么时候不一致—有时风格指南不适用。在编写的过程中如有疑问,还需自行判断,多参考其他例子,并决定什么看起来最好,同时还应多向有经验的程序员请教。

1.代码结构

(1)缩进:每个缩进级别使用4个空格。

(2)制表符或空格:空格是首选的缩进方法,应该避免混合使用制表符和空格。

(3)空行:在Solidity源代码中合约声明之间留出两个空行。正确写法如下。

contract A {

    ...

}

 

contract B {

    ...

}

 

contract C {

    ...

}

错误写法如下。

contract A {

    ...

}

contract B {

    ...

}

 

contract C {

    ...

}

在一个合约中的函数声明之间应该留有一个空行,在相关联的各组单行语句之间可以省略空行(例如抽象合约的stub函数)。正确写法如下。

pargma solidity >=0.4.0 <0.6.0;

 

contract A {

    function spam() public;

    function ham() public;

}

 

 

contract B is A {

    function spam() public {

        ...

    }

 

    function ham() public {

        ...

    }

}

错误写法如下。

pargma solidity >=0.4.0 <0.6.0;

 

contract A {

    function spam() public pure {

        // …

    }

    function ham() public pure {

         // …

    }

}

(4)代码行的最大长度:基于PEP 8 recommendation,代码行的字符长度控制在79(或99)字符,以帮助读者阅读代码。折行时应该遵从以下规则。

第一个参数不应该紧跟在左括号后边。

用一个且只用一个缩进。

每个函数应该单起一行。

结束符号“);”应该单独放在最后一行。

关于函数调用的正确写法:

thisFunctionCallIsReallyLong(

    longArgument1,

    longArgument2,

    longArgument3

);

错误写法:

thisFunctionCallIsReallyLong(longArgument1,

                             longArgument2,

                             longArgument3

);

 

thisFunctionCallIsReallyLong(longArgument1,

    longArgument2,

    longArgument3

);

 

thisFunctionCallIsReallyLong(

    longArgument1, longArgument2,

    longArgument3

);

 

thisFunctionCallIsReallyLong(

longArgument1,

longArgument2,

longArgument3

);

 

thisFunctionCallIsReallyLong(

    longArgument1,

    longArgument2,

    longArgument3);

关于赋值语句的正确写法:

thisIsALongNestedMapping[being][set][to_some_value] = someFunction(

    argument1,

    argument2,

    argument3,

    argument4

);

错误写法:

thisIsALongNestedMapping[being][set][value] = someFunction(argument1,

                                                           argument2,

                                                           argument3,

                                                           argument4);

有关事件定义和事件发生的正确写法:

event LongAndLotsOfArgs(

    adress sender,

    adress recipient,

    uint256 publicKey,

    uint256 amount,

    bytes32[] options

);

 

LongAndLotsOfArgs(

    sender,

    recipient,

    publicKey,

    amount,

    options

);

错误写法:

event LongAndLotsOfArgs(adress sender,

                        adress recipient,

                        uint256 publicKey,

                        uint256 amount,

                        bytes32[] options);

 

LongAndLotsOfArgs(sender,

                  recipient,

                  publicKey,

                  amount,

                  options);

(5)源文件编码格式:首选UTF-8或ASCII编码。

(6)Import规范:Import语句应始终放在文件的顶部。正确写法:

import "owned";

 

contract A {

    ...

}

 

contract B is owned {

    ...

}

错误写法:

contract A {

    ...

}

 

import "owned";

 

contract B is owned {

    ...

}

(7)函数顺序:排序有助于读者识别他们可以调用哪些函数,并更容易地找到构造函数和fallback函数的定义。函数应根据其可见性和顺序进行分组。

构造函数。

fallback函数(如果存在)。

外部函数。

公共函数。

内部函数和变量。

私有函数和变量。

在一个分组中,把view和pure函数放在最后。正确写法:

contract A {

    function A() public {

        ...

    }

 

    function() public {

        ...

    }

 

    // 外部函数

    // ...

 

    // view 类型的外部函数

    // ...

 

    // pure 类型的外部函数

    // ...

 

    // 公共函数

    // ...

 

    // 内部函数

    // ...

 

    // 私有函数

    // ...

}

错误写法:

contract A {

 

    // 外部函数

    // ...

 

    // 私有函数

    // ...

 

    // 公共函数

    // ...

 

    function A() public {

        ...

    }

 

    function() public {

        ...

    }

 

    // Internal functions

    // ...

}

(8)表达式中的空格:除单行函数声明外,紧接着小括号、中括号或者大括号的内容应该避免使用空格。正确写法:

spam(ham[1], Coin({name: "ham"}));

错误写法:

spam( ham[ 1 ], Coin( { name: "ham" } ) );

例外:

spam( ham[ 1 ], Coin( { name: "ham" } ) );

逗号和分号之前不使用空格。正确写法:

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 {

    ...

}

错误写法:

function () public {

    ...

}

(9)控制结构:用大括号表示一个合约、库、函数和结构,应该遵循以下规则。

开括号与声明应在同一行。

闭括号在与之前函数声明对应的开括号保持同一缩进级别上另起一行。

开括号前应该有一个空格。

正确写法:

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;

}

(10)函数声明:对于简短的函数声明,建议函数体的开括号与函数声明保持在同一行。闭大括号应该与函数声明的缩进级别相同。开大括号之前应该有一个空格。正确写法:

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 explicitlyPublic(uint val) public {

    doSomething();

}

错误写法:

function implicitlyPublic(uint val) {

    doSomething();

}

函数的可见性修饰符应该出现在任何自定义修饰符之前。正确写法:

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();

}

多行输出参数和返回值语句应该遵从“代码行的最大长度”部分的说明。正确写法:

function thisFunctionNameIsReallyLong(

    address a,

    address b,

    address c

)

    public

    returns (

        address someAddressName,

        uint256 LongArgument,

        uint256 Argument

    )

{

    doSomething()

 

    return (

        veryLongReturnArg1,

        veryLongReturnArg2,

        veryLongReturnArg3

    );

}

错误写法:

function thisFunctionNameIsReallyLong(

    address a,

    address b,

    address c

)

    public

    returns (address someAddressName,

            uint256 LongArgument,

            uint256 Argument)

{

    doSomething()

 

    return (veryLongReturnArg1,

            veryLongReturnArg1,

            veryLongReturnArg1);

}

对于继承合约中需要参数的构造函数,如果函数声明很长或难以阅读,建议将基础构造函数像多个修饰符的风格那样每个下沉到一个新行上书写。正确写法:

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

    {

        // 有关 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

    {

        // 有关 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 {

        // 有关 param5 的操作

    }

}

当用单个语句声明简短函数时,允许在一行中完成,例如:

function shortFunction() public { doSomething(); }

这些函数声明的准则旨在提高可读性。因为本指南不会涵盖所有内容,读者应该自行作出最佳判断。

(11)变量声明:数组变量的声明在变量类型和括号之间不应该有空格。正确写法:

uint[] x;

错误写法:

uint [] x;

(12)其他建议:字符串应该用双引号而不是单引号。正确写法:

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;

2.命名规范

在一个项目中,当完全采纳和使用命名规范时会产生强大的作用。当使用不同的规范时,则很难立即获取代码中传达的重要元信息。

这里给出的命名建议旨在提高可读性,因此它们不是规则,而是透过名称来尝试和帮助传达更多的信息。

最后,基于代码库中的一致性,本节中的任何规范总是可以被(代码库中的规范)取代。

(1)命名方式:为了避免混淆,下面的名字用来指明不同的命名方式。

b(单个小写字母)。

B(单个大写字母)。

lowercase(小写)。

lower_case_with_underscores(小写和下画线)。

UPPERCASE(大写)。

UPPER_CASE_WITH_UNDERSCORES(大写和下画线)。

CapitalizedWords(驼峰式,首字母大写)。

mixedCase(混合式,与驼峰式的区别在于首字母小写!)。

Capitalized_Words_With_Underscores(首字母大写和下画线)。

需要注意的是,当在驼峰式命名中使用缩写时,应该将缩写中的所有字母都大写。因此HTTPServerError比HttpServerError好。另外,当在混合式命名中使用缩写时,除了第一个缩写中的字母小写(如果它是整个名称的开头)以外,其他缩写中的字母均大写。因此xmlHTTPRequest比XMLHTTPRequest好。

(2)应避免的名称:

l - el的小写方式;

O - oh的大写方式;

I - eye的大写方式。

切勿将这些用于单个字母的变量名称,它们经常难以与数字1和0区分开。

(3)合约和库名称:合约和库名称应该使用驼峰式风格,比如SimpleToken、Smart-Bank、CertificateHashRepository、Player。

(4)结构体名称:结构体名称应该使用驼峰式风格,比如MyCoin、Position、PositionXY。

(5)事件名称:事件名称应该使用驼峰式风格,比如Deposit、Transfer、Approval、BeforeTransfer、AfterTransfer。

(6)函数名称:函数名称不同于结构,应该使用混合式命名风格,比如getBalance、transfer、verifyOwner、addMember、changeOwner。

(7)函数参数名:函数参数命名应该使用混合式命名风格,比如initialSupply、account、recipientAddress、senderAddress、newOwner。在编写操作自定义结构的库函数时,这个结构体应该作为函数的第一个参数,并且应该始终命名为self。

(8)常量命名:常量应该全都使用大写字母书写,并用下划线分割单词,比如MAX_BLOCKS、TOKEN_NAME、TOKEN_TICKER、CONTRACT_VERSION。

(9)修饰符命名:使用混合式命名风格,比如onlyBy、onlyAfter、onlyDuringThe-PreSale。

(10)枚举变量命名:在声明简单类型时,枚举应该使用驼峰式风格,比如Token-Group、Frame、HashStyle、CharacterLocation。

(11)避免命名冲突:当所起名称与内建或保留关键字相冲突时,建议照此惯例在名称后边添加下划线,比如single_trailing_underscore_。


登录后可下载附件,请登录或者注册

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:huaweicloud.bbs@huawei.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
评论文章 //点赞 收藏 0
点赞
分享文章到微博
分享文章到朋友圈

评论 (0)


0/1000
评论

登录后可评论,请 登录注册

评论

您没有权限执行当前操作

温馨提示

您确认删除评论吗?

确定
取消
温馨提示

您确认删除评论吗?

删除操作无法恢复,请谨慎操作。

确定
取消
温馨提示

您确认删除博客吗?

确定
取消

确认删除

您确认删除博客吗?

确认删除

您确认删除评论吗?

温馨提示

登录超时或用户已下线,请重新登录!!!

确定
取消