阅读(1809) (12)

函数类型

2022-05-11 15:30:22 更新

函数类型是函数的类型。函数类型的变量可以从函数中赋值,函数类型的函数参数可以用来传递函数到函数调用和从函数调用返回函数。函数类型有两种形式——内部函数和外部函数:

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

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

函数类型表示如下:

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

与参数类型相比,返回类型不能为空——如果函数类型不应返回任何内容,则 必须省略整个部分。returns (<return types>)

默认情况下,函数类型是内部的,因此internal可以省略关键字。请注意,这仅适用于函数类型。必须为合约中定义的函数明确指定可见性,它们没有默认值。

转换:

函数类型 A 可以隐式转换为函数类型 B 当且仅当: 它们的参数类型相同,返回类型相同,它们的内部/外部属性是相同的,并且 A 的状态可变性比 B 的状态可变性更具限制性,比如:

  • pure函数可以转换为view和non-payable函数
  • view函数可以转换为non-payable函数
  • payable函数可以转换为non-payable函数

函数类型之间没有其他转换是可能的。

关于payableand的规则non-payable可能有点混乱,但本质上,如果一个函数是payable,这意味着它也接受零以太币的支付,所以它也是non-payable。另一方面,non-payable函数会拒绝发送给它的以太币,因此non-payable函数不能转换为payable函数。

如果未初始化函数类型变量,则调用它会导致Panic 错误。如果您在使用后调用函数,也会发生同样的情况delete 。

如果在 Solidity 的上下文之外使用外部函数类型,它们将被视为function类型,它将地址和函数标识符一起编码为一个bytes24类型。

请注意,当前合约的公共功能既可以用作内部功能,也可以用作外部功能。要f用作内部函数,只需使用f,如果要使用其外部形式,请使用this.f。

内部类型的函数可以分配给内部函数类型的变量,而不管它是在哪里定义的。这包括合约和库的私有、内部和公共功能以及免费功能。另一方面,外部函数类型仅与公共和外部合约函数兼容。库被排除在外,因为它们需要一个delegatecall并为其选择器使用不同的 ABI 约定。接口中声明的函数没有定义,因此指向它们也没有意义。

成员:

外部(或公共)函数具有以下成员:

笔记

外部(或公共)函数曾经有额外的成员 .gas(uint)和.value(uint). 这些在 Solidity 0.6.2 中已弃用,并在 Solidity 0.7.0 中删除。而是使用和 分别指定发送到函数的气体量或 wei 量。有关详细信息,请参阅外部函数调用。{gas: ...}{value: ...}

显示如何使用成员的示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.4 <0.9.0;

contract Example {
    function f() public payable returns (bytes4) {
        assert(this.f.address == address(this));
        return this.f.selector;
    }

    function g() public {
        this.f{gas: 10, value: 800}();
    }
}

显示如何使用内部函数类型的示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

library ArrayUtils {
    // internal functions can be used in internal library functions because
    // they will be part of the same code context
    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;
    }
}

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

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;


contract Oracle {
    struct Request {
        bytes data;
        function(uint) external callback;
    }

    Request[] private requests;
    event NewRequest(uint);

    function query(bytes memory data, function(uint) external callback) public {
        requests.push(Request(data, callback));
        emit NewRequest(requests.length - 1);
    }

    function reply(uint requestID, uint response) public {
        // Here goes the check that the reply comes from a trusted source
        requests[requestID].callback(response);
    }
}


contract OracleUser {
    Oracle constant private ORACLE_CONST = Oracle(address(0x00000000219ab540356cBB839Cbe05303d7705Fa)); // known contract
    uint private exchangeRate;

    function buySomething() public {
        ORACLE_CONST.query("USD", this.oracleResponse);
    }

    function oracleResponse(uint response) public {
        require(
            msg.sender == address(ORACLE_CONST),
            "Only oracle can call this."
        );
        exchangeRate = response;
    }
}

笔记

计划使用 Lambda 或内联函数,但尚不支持。