Loading...

智能合约语言 Solidity 教程 – 函数类型

Smart Contracts语言 Solidity 教程 – 函数类型

电报联系方式

智能合约语言 Solidity 教程 - 函数类型

函数类型(Function Types)

在编程中,函数被视为一等公民,这意味着它们可以像其他数据类型一样进行操作。函数类型被视为值类型,因此具备以下特性:

  1. 函数赋值给变量: 可以将函数赋值给函数类型的变量,以便稍后使用。这允许函数在程序中以变量的形式存储和传递。
  2. 函数作为参数传递: 函数可以作为参数传递给其他函数,这使得高阶函数成为可能。高阶函数能够接受其他函数作为输入,从而实现更加灵活的行为。
  3. 返回函数: 在某些编程语言中,你可以在一个函数内部返回另一个函数,这被称为闭包。闭包可以捕获其定义时的上下文信息,使得函数可以记住一些变量的状态。

函数类型有两类:内部(internal)和外部(external)函数

内部(internal)函数只能在当前合约内被调用(在当前的代码块内,包括内部库函数,和继承的函数中)。

外部(external)函数由地址和函数方法签名两部分组成,可作为外部函数调用的参数,或返回值。

函数类型定义如下:

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

函数类型在某些编程环境中默认为 internal,因此通常可以省略 internal 关键字。但在合约(contract)中,函数本身默认为 public,只有当将其用作类型名时才会默认为 internal.

在访问函数时,有两种方式:一种是直接使用函数名(例如 f),这适用于内部函数;另一种是使用 this.f,这适用于外部函数。

如果一个函数变量没有被初始化,尝试调用它将引发异常。同样,如果尝试调用已删除的函数,也会引发相同的异常。

当在Solidity之外的上下文环境中使用合约的外部函数类型时,它们会被视为 function 类型。这种类型将以20字节的合约地址编码,同时附带4字节的函数方法签名,形成一个 bytes24 类型。

在合约内部,公共函数(public)可以使用两种方式进行调用:

  1. 内部方式:使用函数名 f 进行调用。
  2. 外部方式:使用 this.f 进行调用。

这两种方式允许在合约内部以不同的访问权限来调用公共函数,从而实现不同的行为。

成员: 属性 selector

public (或 external) 函数有一个特殊的成员selector, 它对应一个ABI 函数选择器。
“`js
pragma solidity ^0.4.16;

contract Selector {
function f() public view returns (bytes4) {
return this.f.selector;
}
}
“`

下面的代码显示内部(internal)函数类型的使用:

pragma solidity ^0.4.16;

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;
}
}

下面的代码显示外部(external)函数类型的使用:

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 {
// Here goes the check that the reply comes from a trusted source
requests[requestID].callback(response);
}
}

contract OracleUser {
Oracle constant oracle = Oracle(0x1234567); // known contract
function buySomething() {
oracle.query(“USD”, this.oracleResponse);
}
function oracleResponse(bytes response) public {
require(msg.sender == address(oracle));
// Use the data
}
}

函数可见性分析

  • public – 任意访问
  • private – 仅当前合约内
  • internal – 仅当前合约及所继承的合约
  • external – 仅外部访问(在内部也只能用外部访问方式访问)

Example.

pragma solidity^0.4.18;

contract Test {
uint[10] x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

function test(uint[10] a) public returns (uint){
return a[9]*2;
}

function test2(uint[10] a) external returns (uint){
return a[9]*2;
}

function calltest() {
test(x);
}

function calltest2() {
this.test2(x);
//test2(x); //不能在内部调用一个外部函数,会报编译错误。
}

}

打开Remix – Solidity IDE,帖入代码,创建合约。
然后,我们分别调用 test 及 test2 ,对比执行花费的 gas。

智能合约语言 Solidity 教程 - 函数类型

可以看到调用pubic函数花销更大,为什么呢?

当使用 public 函数时,Solidity会立即将数组参数的数据复制到内存中。这是因为 public 函数可能会被内部调用,而内部调用需要数组参数在内存中而不是在 calldata Medium.

相比之下,external 函数被设计为不允许内部调用,因此它直接从 calldata 读取数据,避免了额外的数据复制开销。

因此,如果你确定一个函数仅会在外部访问,最好使用 external 函数类型,以避免不必要的数据复制操作。这样可以提高合约的效率和性能。

同样,calltest2的开销比calltest的开销大很多,这是因为通过this.f()模式调用,会有一个大开销的CALL调用,并且它传参的方式也比内部传递开销更大。

因此,当需要内部调用的时候,请用public.

电报联系方式

 

 

© 版权声明

Related posts

No comments

No comments...