函数类型也是值类型的一种,和C语言中的函数指针类似,用于指向一个函数,可以用于实现回掉等功能。
函数类型分为internal 和external, 标记有internal函数类型只能引用当前contract中的函数,标记有external函数类型可以应用定义在其他contract中的函数。
定义一个函数类型的基本格式如下:
function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
有如下几点需要注意:
- 如果函数类型无返回值,可以将省略returns (<return types>)。
- 函数类型默认为internal类型,internal因此可以省略
- public函数即可以被作为internal的函数类型使用,也可以作为external类型的函数使用。
两个例子:
pragma solidity ^0.4.23;
contract InternalFuncTest {
event Error(bytes32 message);
function caculate(uint p1, uint p2,bool flag) public returns(uint) {
//The next line will get a compile error since this.add would be consideed as a external function
//caculate(p1,p2,flag? this.add : this.sub);
return caculate(p1,p2,flag? add : sub);
}
function caculate(uint p1, uint p2, function (uint,uint) internal returns(uint) func ) returns(uint){
return func(p1,p2);
}
function add(uint p1, uint p2) public returns(uint) {
uint sum = p1 + p2;
if (sum < p1 || sum < p2){
emit Error('overflow');
return 0;
}
return sum;
}
function sub(uint p1, uint p2) public returns(uint) {
return p2 < p1 ? p1 -p2 : p2 - p1;
}
}
上面的例子中,caculate(后一个)的第三个参数是一个internal的函数类型,接受两个uint的参数,并返回一个uint.
pragma solidity ^0.4.23;
contract RequetQueue {
struct Request {
uint id;
function (uint,uint) external callback;
}
uint constant MAX_LENGTH = 5;
Request[MAX_LENGTH] queue;
uint front;
uint rear;
bool isEmpty;
bool isFull;
constructor() public {
front = 0;
rear = 0;
isEmpty = true;
isFull = false;
}
function size() public view returns(uint) {
uint result;
if (rear > front){
result = rear - front;
} else if(rear > front) {
result = rear - front + MAX_LENGTH;
} else {
result = isEmpty ? 0 : MAX_LENGTH;
}
return result;
}
function request(uint id, function(uint,uint) external callback) public returns(bool) {
bool ret = !isFull;
if (!isFull) {
queue[rear].id = id;
queue[rear].callback = callback;
rear = (rear + 1) % MAX_LENGTH;
isFull = rear == front;
isEmpty = false;
}
return ret;
}
function process(function(uint) external returns(uint) handler) public returns(bool) {
bool ret = !isEmpty;
if (!isEmpty) {
Request storage r = queue[front];
front = (front + 1) % MAX_LENGTH;
isFull = false;
isEmpty = rear == front;
uint result = handler(r.id);
r.callback(r.id,result);
}
return ret;
}
}
contract Requester {
event PringResult(address msgSender, uint id, uint ret);
RequetQueue queue;
constructor(address externalFunctionTypeTestAddress) public {
queue = RequetQueue(externalFunctionTypeTestAddress);
}
function request(uint id) public {
//Write like this "test.request(id, callback);" will get a compile error cause with out this callback was considered as a internal function
queue.request(id, this.callback);
}
function callback(uint id, uint ret) external {
emit PringResult(msg.sender,id,ret);
}
}
contract Processer {
event Handled(address msgSender, uint val);
RequetQueue queue;
constructor(address externalFunctionTypeTestAddress) public {
queue = RequetQueue(externalFunctionTypeTestAddress);
}
function process() public {
queue.process(this.handler);
}
function handler(uint val) public returns(uint) {
emit Handled(msg.sender,val);
return val + 1;
}
}
上面的例子RequetQueue定义了一个循环队列,request和process方法分别接受一个external类型,包含一个uint参数,返回值为bool类型的函数。