专栏首页区块链入门【易错概念】以太坊Solidity函数的external/internal,public/private,view/pure/payable区别

【易错概念】以太坊Solidity函数的external/internal,public/private,view/pure/payable区别

1. 函数类型:内部(internal)函数和外部(external)函数

函数类型是一种表示函数的类型。可以将一个函数赋值给另一个函数类型的变量,也可以将一个函数作为参数进行传递,还能在函数调用中返回函数类型变量。 函数类型有两类:- 内部(internal)函数和 外部(external) 函数:

内部函数只能在当前合约内被调用(更具体来说,在当前代码块内,包括内部库函数和继承的函数中),因为它们不能在当前合约上下文的外部被执行。 调用一个内部函数是通过跳转到它的入口标签来实现的,就像在当前合约的内部调用一个函数。

外部函数由一个地址和一个函数签名组成,可以通过外部函数调用传递或者返回。

函数类型表示成如下的形式

function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]

与参数类型相反,返回类型不能为空 —— 如果函数类型不需要返回,则需要删除整个 returns (<return types>) 部分。

函数类型默认是内部函数,因此不需要声明 internal 关键字。 与此相反的是,合约中的函数本身默认是 public 的,只有当它被当做类型名称时,默认才是内部函数。

有两种方法可以访问当前合约中的函数:一种是直接使用它的名字,f ,另一种是使用 this.f 。 前者适用于内部函数,后者适用于外部函数。

如果当函数类型的变量还没有初始化时就调用它的话会引发一个异常。 如果在一个函数被 delete之后调用它也会发生相同的情况。

如果外部函数类型在 Solidity 的上下文环境以外的地方使用,它们会被视为 function 类型。 该类型将函数地址紧跟其函数标识一起编码为一个 bytes24 类型。。

请注意,当前合约的 public 函数既可以被当作内部函数也可以被当作外部函数使用。 如果想将一个函数当作内部函数使用,就用 f 调用,如果想将其当作外部函数,使用 this.f

除此之外,public(或 external)函数也有一个特殊的成员变量称作 selector,可以返回 ABI 函数选择器:

pragma solidity ^0.4.16;

contract Selector {
  function f() public view returns (bytes4) {
    return this.f.selector;
  }
}

如果使用内部函数类型的例子:

pragma solidity ^0.4.16;

library ArrayUtils {
  // 内部函数可以在内部库函数中使用,
  // 因为它们会成为同一代码上下文的一部分
  function map(uint[] memory self, function (uint) pure returns (uint) f)
    internal
    pure
    returns (uint[] memory r)
  {
    r = new uint[](self.length);
    for (uint i = 0; i < self.length; i++) {
      r[i] = f(self[i]);
    }
  }
  function reduce(
    uint[] memory self,
    function (uint, uint) pure returns (uint) f
  )
    internal
    pure
    returns (uint r)
  {
    r = self[0];
    for (uint i = 1; i < self.length; i++) {
      r = f(r, self[i]);
    }
  }
  function range(uint length) internal pure returns (uint[] memory r) {
    r = new uint[](length);
    for (uint i = 0; i < r.length; i++) {
      r[i] = i;
    }
  }
}

contract Pyramid {
  using ArrayUtils for *;
  function pyramid(uint l) public pure returns (uint) {
    return ArrayUtils.range(l).map(square).reduce(sum);
  }
  function square(uint x) internal pure returns (uint) {
    return x * x;
  }
  function sum(uint x, uint y) internal pure returns (uint) {
    return x + y;
  }
}

另外一个使用外部函数类型的例子:

pragma solidity ^0.4.11;

contract Oracle {
  struct Request {
    bytes data;
    function(bytes memory) external callback;
  }
  Request[] requests;
  event NewRequest(uint);
  function query(bytes data, function(bytes memory) external callback) public {
    requests.push(Request(data, callback));
    NewRequest(requests.length - 1);
  }
  function reply(uint requestID, bytes response) public {
    // 这里要验证 reply 来自可信的源
    requests[requestID].callback(response);
  }
}

contract OracleUser {
  Oracle constant oracle = Oracle(0x1234567); // 已知的合约
  function buySomething() {
    oracle.query("USD", this.oracleResponse);
  }
  function oracleResponse(bytes response) public {
    require(msg.sender == address(oracle));
    // 使用数据
  }
}

注解 Lambda 表达式或者内联函数的引入在计划内,但目前还没支持。

2. 函数可见性说明符:public,private,external,internal

  • public:内部、外部均可见(参考为存储/状态变量创建 getter 函数
  • private:仅在当前合约内可见
  • external:仅在外部可见(仅可修饰函数)——就是说,仅可用于消息调用(即使在合约内调用,也只能通过 this.func 的方式)
  • internal:仅在内部可见(也就是在当前 Solidity 源代码文件内均可见,不仅限于当前合约内,译者注)

函数可见性说明符格式:

function myFunction() <visibility specifier> returns (bool) {
    return true;
}

3. 函数修改器

pure 修饰函数时:不允许修改或访问状态——但目前并不是强制的。 view 修饰函数时:不允许修改状态——但目前不是强制的。 payable 修饰函数时:允许从调用中接收 以太币Ether 。 constant 修饰状态变量时:不允许赋值(除初始化以外),不会占据 存储插槽storage slot 。 constant 修饰函数时:与 view 等价。 anonymous 修饰事件时:不把事件签名作为 topic 存储。 indexed 修饰事件时:将参数作为 topic 存储。

4. 参考

Solidity中文帮助文档

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【易错概念】Solidity语法的合约/抽象合约/接口/库的定义

    以太坊智能合约语言Solitidy是一种面向对象的语言,本文清楚合约定义,以及派生的抽象合约,接口,库的定义。

    辉哥
  • 第十二课 SOLIDITY语法难点解析及故障排查

    (1)推荐编辑器 目前尝试 Solidity 编程的最好的方式是使用 Remix (需要时间加载,请耐心等待)。Remix 是一个基于 Web 的 IDE,它...

    辉哥
  • 链安科技:智能合约call、delegatecall底层函数使用需谨慎

    2018年5月11日ATN Token遭受恶意攻击(详见:https://atn.io/resource/aareport.pdf),攻击者利用DSAuth库与...

    辉哥
  • 第23讲 函数层面的优化

    谈到函数,我们会很自然地联想到Verilog中的module或者VHDL中的entity,没错,它们之间是相互对应的。通常在Vivado HLS中,每个函数会生...

    Lauren的FPGA
  • Windows客户端C/C++编程规范“建议”——函数

    等级:【要求】 说明:每个函数的代码行数控制应该控制在80行以内。如果超过这个限制函数内部逻辑一般可以拆分。如果试图超过这个标准,请列出理由。但理由不包含如下...

    方亮
  • Unity基础系列(三)——数学表面(数字雕刻)

    在完成前面的教程之后,我们有一个基于线条的视图,并在游戏模式下显示一个正弦波动画。当然还可以通过修改代码来显示其他数学函数。甚至可以在Unity编辑器处于播放模...

    放牛的星星
  • 说说Python中闭包是什么?

    答:可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,外函数和内函数。在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返...

    用户1564362
  • JS 计时器参数剖析与真题

    HTML5学堂-码匠:计时器的第一个参数,包含几种不同的书写方法,可以是函数名,匿名函数,JS代码字符串,还有一些面试题当中会出现“函数调用”的书写方式。 那么...

    HTML5学堂
  • 深入解析golang编程中函数的用法

    函数是一组一起执行任务的语句。每Go程序具有至少一个函数,它一般是main(),以及所有的最琐碎程序可以定义附加函数。 你可以将代码放到独立的功能。如何划分代码...

    李海彬
  • 深入理解JavaScript中的函数

    函数于软件开发者而言并不是什么奇幻世界。如果你的日常活动涉及到编码,哪怕是一点点,那么在一天结束的时候,你一定创建/修改了一个或多个函数。

    疯狂的技术宅

扫码关注云+社区

领取腾讯云代金券