Solidity——函数

133 阅读4分钟

Solidity 中函数的形式:

function <function name>([parameter types[, ...]]) {internal|external|public|private} [pure|view|payable] [virtual|override] [<modifiers>]
[returns (<return types>)]{ <function body> }
  1. function:声明函数时的固定用法。要编写函数,就需要以 function 关键字开头。
  2. <function name>:函数名。
  3. ([parameter types[, ...]]):圆括号内写入函数的参数,即输入到函数的变量类型和名称。
  4. {internal|external|public|private}:函数可见性说明符,共有4种。
  • public:内部和外部均可见。

  • private:只能从本合约内部访问,继承的合约也不能使用。

  • external:只能从合约外部访问(但内部可以通过 this.f() 来调用,f是函数名)。

  • internal: 只能从合约内部访问,继承的合约可以用。

    注意 1:合约中定义的函数需要明确指定可见性,它们没有默认值。注意 2public|private|internal 也可用于修饰状态变量。public变量会自动生成同名的getter函数,用于查询数值。未标明可见性类型的状态变量,默认为internal

  1. [pure|view|payable]:决定函数权限/功能的关键字。payable(可支付的)很好理解,带着它的函数,运行的时候可以给合约转入 ETH。pure 和 view 的介绍见下一节。
  2. [virtual|override]: 方法是否可以被重写,或者是否是重写方法。virtual用在父合约上,标识的方法可以被子合约重写。override用在自合约上,表名方法重写了父合约的方法。
  3. <modifiers>: 自定义的修饰器,可以有0个或多个修饰器。
  4. [returns ()]:函数返回的变量类型和名称。
  5. <function body>: 函数体。

到底什么是 Pure 和View

以太坊交易需要支付气费(gas fee),合约的状态变量存储在链上,gas fee很贵,如果计算不改变链上状态就不需要支付gas,包含PureView关键字的函数是不改变链上状态的,因此用户直接调用它们是不需要付 gas 的。

被视为改写链上状态的语句

  1. 写入状态变量
  2. 释放事件
  3. 创建其他合约
  4. 使用 selfdestruct.
  5. 通过调用发送以太币
  6. 调用任何未标记 view 或 pure 的函数。
  7. 使用低级调用(low-level calls)。
  8. 使用包含某些操作码的内联汇编。

代码

  1. pure 和 view
    我们在合约里定义一个状态变量 number,初始化为 5。

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.2;
    contract FunctionTypes{
        uint256 public number = 5;
    }
    

    定义一个 add() 函数,每次调用会让 number 增加 1。

    // 默认function
    function add() external {
        number = number + 1;
    } 
    

    如果 add() 函数被标记为 pure,比如 function add() external pure,就会报错。因为 pure 是不配读取合约里的状态变量的,更不配改写。那 pure 函数能做些什么?举个例子,你可以给函数传递一个参数 _number,然后让他返回 _number + 1,这个操作不会读取或写入状态变量。

    function addPure(uint256 _number) external pure returns(uint256 new_number){
        new_number = _number + 1;
    }
    

    如果 add() 函数被标记为 view,比如 function add() external view,也会报错。因为 view 能读取,但不能够改写状态变量。我们可以稍微改写下函数,读取但是不改写 number,返回一个新的变量。

    function addView() external view returns(uint256 new_number) {
        new_number = number + 1;
    }
    
  2. internal v.s. external

    // internal: 内部函数
    function minus() internal {
        number = number - 1;
    }
    
    // 合约内的函数可以调用内部函数
    function minusCall() external {
        minus();
    }
    

    我们定义一个 internal 的 minus() 函数,每次调用使得 number 变量减少 1。由于 internal 函数只能由合约内部调用,我们必须再定义一个 external 的 minusCall() 函数,通过它间接调用内部的 minus() 函数。

  3. payable

// payable: 递钱,能给合约支付eth的函数
function minusPayable() external payable returns(uint256 balance) { 
    minus(); 
    balance = address(this).balance; 
}

我们定义一个 external payable 的 minusPayable() 函数,间接的调用 minus(),并且返回合约里的 ETH 余额(this 关键字可以让我们引用合约地址)。我们可以在调用 minusPayable() 时往合约里转入1个 ETH。

总结

view 函数可以读取状态变量,但不能改写;pure 函数既不能读取也不能改写状态变量。