15

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

 4 years ago
source link: https://learnblockchain.cn/article/1184
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Solidity 0.6.x 版本中 , 继承的一些新变化。

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

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

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

显式使用 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)函数“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 )的函数都是隐式虚函数的,因此在实现接口时,必须在实现中显式重写其函数。 这里 有关此设计的讨论。

值得注意的是,关键字 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 会溢出。

而在 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

本翻译由登链社区及 Cell Network 赞助。

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

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

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

显式使用 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)函数“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 )的函数都是隐式虚函数的,因此在实现接口时,必须在实现中显式重写其函数。 这里 有关此设计的讨论。

值得注意的是,关键字 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 会溢出。

而在 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

本翻译由登链社区及 Cell Network 赞助。

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 33分钟前
  • 阅读 ( 17 )
  • 学分 ( 0 )
  • 分类:Solidity

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK