由登链社区翻译的 Solidity 中文文档 1 已经通过更新到 0.6.0.

与面向对象编程类似,Solidity 是一种面向合约的语言,广泛使用继承和多态,并且对于语言的发展至关重要。Solidity 开发人员如果不使用这些特性很难分离逻辑及增加代码重用性。

使用 Solidity 0.6 版时,引入的主要改进除了引入接口继承、禁止状态变量屏蔽之外,还使现有规则更明确。编译器继续使用 C3 线性化,有关继承请参见 Solidity 中文文档-继承 2

显式使用 virtualoverride

默认情况下,函数不再是虚函数 (virtual) 。这意味着调用非虚拟函数将始终执行该函数,而不管它的继承层次结构的其他合约。这减少了在 solidity 0.5 中存在的歧义,在 solidity 0.5 版本中的所有函数都是隐式虚函数,从而可以在继承结构中进一步重写。这在大型继承中尤其危险,在这种情况下,这种歧义可能导致意外的行为和错误。

例如,在下面的合约 C 中,调用 setValue 会调用最派生合约 B 的实现(因为 B 是继承关系的最后一个),但这在实现中并不明显。

                      *
    pragma solidity ^0.5.17;contract A {    uint public x;    function setValue(uint_x) public {        x =_x;    }}  
    contract B {    uint public y;    function setValue(uint_y) public {        y =_y;    }}  
    contract C is A, B {}

而上面示例使用 0.6 版编译时,编译器会报这样一个错误 : Derived contract must override function "setValue". Two or more base classes define functions with the same name and parameter types

意思是:因为父合约定义具有相同名称和参数类型的函数,派生合约必须重写(override[3])函数“setValue”。在上面多重继承的示例中,有同一个函数是从多个父合约(合约 A 和 B)继承。在这种情况下,必须要重写,并且必须 override 修饰符中列出父合约。要注意重要的一点,override(A,B) 中的顺序无关紧要, 它不会改变 super 的行为, super 仍然由继承图的 C3 线性化决定,即继承关系由 contract C is A, B { ... } 声明的顺序决定。

    pragma solidity ^0.6.10;contract A {    uint public x;    function setValue(uint_x) public virtual {        x =_x;    }}  
    contract B {    uint public y;    function setValue(uint_y) public virtual {        y =_y;    }}  
    contract C is A, B {    function setValue(uint_x) public override(A,B) {        A.setValue(_x);    }}

请注意,只有标记为 virtual 的函数才可以重写它们。此外,任何重写的函数都必须标记为 override 。如果重写后依旧是可重写的,则仍然需要标记为 virtual(译者注:也就是有 overridevritual 两个关键字标记符)。

接口(interface)的函数都是隐式虚函数的,因此在实现接口时,必须在实现中显式重写其函数。 这里 [4] 有关此设计的讨论。

值得注意的是,关键字 super 的工作原理与以前相同:在扁平化继承层次结构中,super 将函数调用到更上一级的函数。外部函数(external 函数)仍然不允许使用 super

接口可以继承

这个是 solidity 0.6 新增的功能,允许接口继承接口。派生的接口是的所有接口函数的组合。实现合约必须实现的所有继承接口的函数。

    pragma solidity ^0.6.10;interface X {    function setValue(uint_x) external;}  
    interface Y is X {    function getValue() external returns (uint);}  
    contract Z is Y {    uint x;    function setValue(uint_x) external override { x =_x; }    function getValue() external override returns (uint) { return x; }}

请注意,如果合约未实现所有函数,则必须将合约标记为 abstract

    *
    pragma solidity ^0.6.10;abstract contract Z is Y {    uint x;    function setValue(uint_x) external override { x =_x; }}

抽象合约

在 solidity 0.5 版中,编译器隐式地将未实现其所有函数的合约当作是抽象合约。

    pragma solidity ^0.5.17;contract X {    function setValue(uint_x) public virtual;}

而在 solidity 0.6,必须显式指定,否则编译器会报错: contract X should be made abstract

意思是合约 x 应该标记为 abstract。

    pragma solidity ^0.6.10;abstract contract X {    function setValue(uint_x) public virtual;}

公共变量会更安全重写外部函数

尽管此功能在 0.6 之前就已存在,但现在更加安全,0.6 会检查编译器生成 getter 函数与外部函数的参数和返回类型是否匹配。在 0.5 版本,可能允许它们有所不同,如以下示例所示:

    pragma solidity ^0.5.17;interface A{    function f() external pure returns(uint8);}  
    contract B is A{    uint256 public f = 257;}

在 A 接口上调用 B 会返回 1 , 因为 257 转换为 uint8 会溢出 [5]。

而在 solidity 0.6 会产生错误 TypeError: Overriding public state variable return types differ (类型错误:重写的公共变量返回了不同的类型),从而强制我们解决冲突避免溢出。

    pragma solidity ^0.6.10;interface A{    function f() external pure returns(uint256);}  
    contract B is A{    uint256 public override f = 257;}

注意 public 的状态变量仅仅可以重写外部函数( external )并且仍然不允许变量重写 internalpublic 函数。

不再有状态变量遮蔽

在 0.5 版本编译器中允许继承具有相同名称的可见状态变量(仅在某些静态分析工具中提示此问题)。下面的示例演示此设计的问题:

    pragma solidity ^0.5.17;contract A {    uint public x;  
        function setValue1(uint_x) public { x =_x; }}  
    contract B is A {    uint public x;  
        function setValue2(uint_x) public { x =_x; }}

在上面的例子中,A B 各自有自己的 x, 因此,调用 B.setValue2(100) 的结果将是将 B.x 设置为 100,而调用 B.setValue1(200) 的设置将 A.x 设置为 200。

而对于“ B”,则是“ B.x”。因此,调用 B.setValue2 (100)的结果将是将 B.x 设置为 100,而调用 B.setValue1 (200)的设置将 A.x 设置为 200。

现在 0.6 版本禁止这种用法,并引发编译器错误提示:DeclarationError: Identifier already declared (意思是变量已经声明);

原文:https://solidity.ethereum.org/2020/06/18/solidity-0.6-inheritance/

Posted by Elena Gesheva on June 18, 2020

本翻译由登链社区 [6] 及 Cell Network[7] 赞助。

References

[1] Solidity 中文文档 : https://learnblockchain.cn/docs/solidity/
[2] Solidity 中文文档-继承 : https://learnblockchain.cn/docs/solidity/contracts.html#index-16
[3] override: https://learnblockchain.cn/docs/solidity/contracts.html#overriding
[4] 这里 : https://github.com/ethereum/solidity/issues/8281
[5] 溢出 : https://learnblockchain.cn/tags/%E6%95%B4%E6%95%B0%E6%BA%A2%E5%87%BA
[6] 登链社区 : https://learnblockchain.cn
[7] Cell Network: https://www.cellnetwork.io/?utm_souce=learnblockchain


本文作者:登链社区翻译小组

作者主页:

https://learnblockchain.cn/people/412


【译】Solidity 0.6.x 更新:继承

来源链接:mp.weixin.qq.com