用户定义的值类型
用户定义的值类型允许在基本值类型上创建零成本抽象。这类似于别名,但具有更严格的类型要求。
用户定义的值类型使用 定义,其中是新引入类型的名称,并且必须是内置值类型(“基础类型”)。该函数 用于从底层类型转换为自定义类型。同样,该函数用于从自定义类型转换为基础类型。type C is V
C
V
C.wrap
C.unwrap
该类型C
没有任何运算符或绑定的成员函数。特别是,连运算符==
都没有定义。不允许与其他类型进行显式和隐式转换。
这种类型的值的数据表示是从底层类型继承的,底层类型也在 ABI 中使用。
以下示例说明了一个自定义类型,该类型UFixed256x18
表示具有 18 个小数的小数定点类型和一个用于对该类型进行算术运算的最小库。
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; // Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. type UFixed256x18 is uint256; /// A minimal library to do fixed point operations on UFixed256x18. library FixedMath { uint constant multiplier = 10**18; /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked /// arithmetic on uint256. function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) { return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b)); } /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked /// arithmetic on uint256. function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) { return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b); } /// Take the floor of a UFixed256x18 number. /// @return the largest integer that does not exceed `a`. function floor(UFixed256x18 a) internal pure returns (uint256) { return UFixed256x18.unwrap(a) / multiplier; } /// Turns a uint256 into a UFixed256x18 of the same value. /// Reverts if the integer is too large. function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { return UFixed256x18.wrap(a * multiplier); } }
注意UFixed256x18.wrap
和如何FixedMath.toUFixed256x18
具有相同的签名但执行两个非常不同的操作:UFixed256x18.wrap
函数返回UFixed256x18
与输入具有相同数据表示的 a,而toUFixed256x18
返回 UFixed256x18
具有相同数值的 a。
函数类型
函数类型是函数的类型。函数类型的变量可以从函数中赋值,函数类型的函数参数可以用来传递函数到函数调用和从函数调用返回函数。函数类型有两种形式——内部函数和外部函数:
内部函数只能在当前合约内部调用(更具体地说,在当前代码单元内部,还包括内部库函数和继承函数),因为它们不能在当前合约上下文之外执行。调用内部函数是通过跳转到它的入口标签来实现的,就像在内部调用当前合约的函数一样。
外部函数由地址和函数签名组成,它们可以通过外部函数调用传递和返回。
函数类型表示如下:
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
函数
函数类型之间没有其他转换是可能的。
关于payable
and的规则non-payable
可能有点混乱,但本质上,如果一个函数是payable
,这意味着它也接受零以太币的支付,所以它也是non-payable
。另一方面,non-payable
函数会拒绝发送给它的以太币,因此non-payable
函数不能转换为payable
函数。
如果未初始化函数类型变量,则调用它会导致Panic 错误。如果您在使用后调用函数,也会发生同样的情况delete
。
如果在 Solidity 的上下文之外使用外部函数类型,它们将被视为function
类型,它将地址和函数标识符一起编码为一个bytes24
类型。
请注意,当前合约的公共功能既可以用作内部功能,也可以用作外部功能。要f
用作内部函数,只需使用f
,如果要使用其外部形式,请使用this.f
。
内部类型的函数可以分配给内部函数类型的变量,而不管它是在哪里定义的。这包括合约和库的私有、内部和公共功能以及免费功能。另一方面,外部函数类型仅与公共和外部合约函数兼容。库被排除在外,因为它们需要一个delegatecall
并为其选择器使用不同的 ABI 约定。接口中声明的函数没有定义,因此指向它们也没有意义。
成员:
外部(或公共)函数具有以下成员:
-
.address
返回函数合约的地址。 -
.selector
返回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 或内联函数,但尚不支持。