用戶定義的值類型允許在基本值類型上創(chuàng)建零成本抽象。這類似于別名,但具有更嚴(yán)格的類型要求。
用戶定義的值類型使用 定義,其中是新引入類型的名稱,并且必須是內(nèi)置值類型(“基礎(chǔ)類型”)。該函數(shù) 用于從底層類型轉(zhuǎn)換為自定義類型。同樣,該函數(shù)用于從自定義類型轉(zhuǎn)換為基礎(chǔ)類型。type C is V
C
V
C.wrap
C.unwrap
該類型C
沒有任何運(yùn)算符或綁定的成員函數(shù)。特別是,連運(yùn)算符==
都沒有定義。不允許與其他類型進(jìn)行顯式和隱式轉(zhuǎn)換。
這種類型的值的數(shù)據(jù)表示是從底層類型繼承的,底層類型也在 ABI 中使用。
以下示例說明了一個自定義類型,該類型UFixed256x18
表示具有 18 個小數(shù)的小數(shù)定點(diǎn)類型和一個用于對該類型進(jìn)行算術(shù)運(yùn)算的最小庫。
// 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
具有相同的簽名但執(zhí)行兩個非常不同的操作:UFixed256x18.wrap
函數(shù)返回UFixed256x18
與輸入具有相同數(shù)據(jù)表示的 a,而toUFixed256x18
返回 UFixed256x18
具有相同數(shù)值的 a。
函數(shù)類型是函數(shù)的類型。函數(shù)類型的變量可以從函數(shù)中賦值,函數(shù)類型的函數(shù)參數(shù)可以用來傳遞函數(shù)到函數(shù)調(diào)用和從函數(shù)調(diào)用返回函數(shù)。函數(shù)類型有兩種形式——內(nèi)部函數(shù)和外部函數(shù):
內(nèi)部函數(shù)只能在當(dāng)前合約內(nèi)部調(diào)用(更具體地說,在當(dāng)前代碼單元內(nèi)部,還包括內(nèi)部庫函數(shù)和繼承函數(shù)),因?yàn)樗鼈儾荒茉诋?dāng)前合約上下文之外執(zhí)行。調(diào)用內(nèi)部函數(shù)是通過跳轉(zhuǎn)到它的入口標(biāo)簽來實(shí)現(xiàn)的,就像在內(nèi)部調(diào)用當(dāng)前合約的函數(shù)一樣。
外部函數(shù)由地址和函數(shù)簽名組成,它們可以通過外部函數(shù)調(diào)用傳遞和返回。
函數(shù)類型表示如下:
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
與參數(shù)類型相比,返回類型不能為空——如果函數(shù)類型不應(yīng)返回任何內(nèi)容,則 必須省略整個部分。returns (<return types>)
默認(rèn)情況下,函數(shù)類型是內(nèi)部的,因此internal
可以省略關(guān)鍵字。請注意,這僅適用于函數(shù)類型。必須為合約中定義的函數(shù)明確指定可見性,它們沒有默認(rèn)值。
轉(zhuǎn)換:
當(dāng)且僅當(dāng)它們的參數(shù)類型相同、返回類型相同、內(nèi)部/外部屬性相同并且 的狀態(tài)可變性 比 的狀態(tài)可變性更具限制性時,函數(shù)類型A
才能隱式轉(zhuǎn)換為函數(shù)類型。尤其:B
A
B
pure
函數(shù)可以轉(zhuǎn)換為view
和non-payable
函數(shù)
view
函數(shù)可以轉(zhuǎn)換為non-payable
函數(shù)
payable
函數(shù)可以轉(zhuǎn)換為non-payable
函數(shù)
函數(shù)類型之間沒有其他轉(zhuǎn)換是可能的。
關(guān)于payable
and的規(guī)則non-payable
可能有點(diǎn)混亂,但本質(zhì)上,如果一個函數(shù)是payable
,這意味著它也接受零以太幣的支付,所以它也是non-payable
。另一方面,non-payable
函數(shù)會拒絕發(fā)送給它的以太幣,因此non-payable
函數(shù)不能轉(zhuǎn)換為payable
函數(shù)。
如果未初始化函數(shù)類型變量,則調(diào)用它會導(dǎo)致Panic 錯誤。如果您在使用后調(diào)用函數(shù),也會發(fā)生同樣的情況delete
。
如果在 Solidity 的上下文之外使用外部函數(shù)類型,它們將被視為function
類型,它將地址和函數(shù)標(biāo)識符一起編碼為一個bytes24
類型。
請注意,當(dāng)前合約的公共功能既可以用作內(nèi)部功能,也可以用作外部功能。要f
用作內(nèi)部函數(shù),只需使用f
,如果要使用其外部形式,請使用this.f
。
內(nèi)部類型的函數(shù)可以分配給內(nèi)部函數(shù)類型的變量,而不管它是在哪里定義的。這包括合約和庫的私有、內(nèi)部和公共功能以及免費(fèi)功能。另一方面,外部函數(shù)類型僅與公共和外部合約函數(shù)兼容。庫被排除在外,因?yàn)樗鼈冃枰粋€delegatecall
并為其選擇器使用不同的 ABI 約定。接口中聲明的函數(shù)沒有定義,因此指向它們也沒有意義。
成員:
外部(或公共)函數(shù)具有以下成員:
.address
返回函數(shù)合約的地址。
.selector
返回ABI 函數(shù)選擇器
筆記
外部(或公共)函數(shù)曾經(jīng)有額外的成員 .gas(uint)
和.value(uint)
. 這些在 Solidity 0.6.2 中已棄用,并在 Solidity 0.7.0 中刪除。而是使用和 分別指定發(fā)送到函數(shù)的氣體量或 wei 量。有關(guān)詳細(xì)信息,請參閱外部函數(shù)調(diào)用。{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}(); } }
顯示如何使用內(nèi)部函數(shù)類型的示例:
// 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; } }
另一個使用外部函數(shù)類型的例子:
// 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 或內(nèi)聯(lián)函數(shù),但尚不支持。
更多建議: