Solidity函数的困惑


  关于Solidity我看的是不明不白,主要是web3的api几乎一无所知,而且对区块链的理解也不够深刻,在此记录一下一些令我窒息的语法糖。

1. 关于函数的可见型与访问控制

  Solidity封装了两种函数调用方式 internalexternal

  • internal

  internal调用,实现时转为简单的EVM跳转,所以他能够直接访问上下文的数据,对于引用传递是十分高效,例如memory之间的值传递,实际上是引用的传递(妈耶,storage和memory又是坑,不同版本真是令人窒息)。

  当前代码单元内,比如同一个合约内的函数,引入的library库,以及父类函数的直接调用即为internal调用,比如:

1
2
3
4
5
6
7
8
9
pragma solidity >=0.4.0 < 0.6.0;

contract test{
function a() internal {}

function b() internal {
a();
}
}

  在上述代码中的b()对a()的调用即为internal方式调用,函数在不显式声明访问类型时,以目前的版本来看会报错。

  • external

  external调用实现了合约的外部消息调用。所以合约在初始化时不能以external的方式调用自身函数,因为此时合约仍未构造完成,此处可类比struct类型,一个结构体不能包含自身对象。但是可以以this的方式强制进行external调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity >= 0.4.0 < 0.6.0;
contract test{
function a() external {}

function b() public {
a(); //此时会报错
}

contract ext{
function callA(test tmp) public {
tmp.a();
}
}
}
  • public

  public的特点是,函数既可以以internal方式调用,也可以用internal方式调用。public函数可以被外部接口访问,是合约对外接口的一部分。

1
2
3
4
5
6
7
8
9
10
pragma solidity >= 0.4.0 < 0.6.0

contract test{
function fun1() public{}

funciton fun2() public {
fun1();
this.fun2();
}
}

  可以看到没有报错,既然public这么舒服,那为啥我还要用external???

  经过对比后我们可以发现,external方法消耗的gas要比public少,因为Solidity在调用public函数时会将代码复制到EVM的内存中,而external则是以calldata的方式进行调用的。内存分配在EVM中是十分宝贵的,而读取calldata则十分廉价,因此在处理大量外部数据,并反复调用函数时,应当考虑用external方法。

  这里应当注意的是,public属于可见性。函数的可见性分为四种:public private internal external .

  • private

  对于private,与internal的区别是,private的方法在子类中无法调用,即使被声明为private也不能阻止数据的查看。访问权限仅仅是限制其他合约对函数的访问和数据修改的权限。而private方法也默认以internal的方式调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pragma solidity >= 0.4.0 < 0.6.0;

contract test{
function fun1() private{}

function fun2() public{
fun1();
//this.fun1()
}
}

//合约的继承为is,这一点很容易理解,如果你明白设计模式的话,实际上继承是A is B 的关系,我很喜欢这种写法。

contract ext is test{
function callFun() public {
//fun1();
fun2();
}
}

  这里我们可以明确的看到private的效果,和internal类似,但是代价会更大。

  然而 publicprivate 还可以被作用于其他的变量,用于设置外部访问权限。

  请大家务必不要弄混 调用方式可见性(visable)

  • this

  在Solidity中,this与其他高级语言意义不同,这里的this指的是当前合约的一个实例化对象,而并不是只的合约本身,this可以理解为实现external调用的一种方式,在初始化未完成时强制调用external类型方法。而并不能指代当前合约类型。

1
2
3
4
5
6
7
8
9
pragma solidity >= 0.4.0 < 0.6.0;

contract test{
function fun1() external{}

function fun2() public{
this.fun1();
}
}

  • getter

  编译器会为公共状态变量提供一个getter(访问器)函数,对mapping和数组以及枚举类型也提供了对应的getter,mapping的key 数组的下标 枚举的名都具有getter,访问器的visable为external。


2. 关于 view pure constant

  在0.4.1之前只有constant这一种可爱的语法,就是有一些屁事很多的人觉得constant指的是变量,作用于函数不太合适,所以就把constant拆成了view和pure。

  在Solidity中,constant view pure 的作用是告诉编译器,函数 不改变不读取状态变量,这样一来函数的执行就不再消耗gas了,因为不再需要矿工去验证。

  然而这三个东西有点有意思,在官方文档中用 restrictive 这一词来对函数的严格性进行描述,在函数类型转换时对严格行有一定的要求,高严格性函数可以被转化为低严格性函数:

  • pure 类型可被转化为 viewnon-payable 函数

  • view 类型可被转化为 non-payable 函数

  • payable 类型可被转化为 non-payable 函数

  真是令人头秃!

toutu