1. 函數類型:內部(internal)函數和外部(external)函數
函數類型是一種表示函數的類型。可以將一個函數賦值給另一個函數類型的變量,也可以將一個函數作為參數進行傳遞,還能在函數調用中返回函數類型變量。 函數類型有兩類:- 內部(internal)函數和 外部(external) 函數:
內部函數只能在當前合約內被調用(更具體來說,在當前代碼塊內,包括內部庫函數和繼承的函數中),因為它們不能在當前合約上下文的外部被執行。 調用一個內部函數是通過跳轉到它的入口標簽來實現的,就像在當前合約的內部調用一個函數。
外部函數由一個地址和一個函數簽名組成,可以通過外部函數調用傳遞或者返回。
函數類型表示成如下的形式
function () {internal|external} [pure|constant|view|payable] [returns ()]
與參數類型相反,返回類型不能為空 —— 如果函數類型不需要返回,則需要刪除整個 returns () 部分。
函數類型默認是內部函數,因此不需要聲明 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() returns (bool) {
return true;
}
3. 函數修改器
pure 修飾函數時:不允許修改或訪問狀態——但目前并不是強制的。
view 修飾函數時:不允許修改狀態——但目前不是強制的。
payable 修飾函數時:允許從調用中接收 以太幣Ether 。
constant 修飾狀態變量時:不允許賦值(除初始化以外),不會占據 存儲插槽storage slot 。
constant 修飾函數時:與 view 等價。
anonymous 修飾事件時:不把事件簽名作為 topic 存儲。
indexed 修飾事件時:將參數作為 topic 存儲。
4. 參考