solidity中令人窒息的语法糖

Solidity函数的困惑


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

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

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

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

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

pragma solidity >=0.4.0 < 0.6.0;

contract test{
    function a() internal {}

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

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

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

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的特点是,函数既可以以internal方式调用,也可以用internal方式调用。public函数可以被外部接口访问,是合约对外接口的一部分。

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

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)

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

pragma solidity >= 0.4.0 < 0.6.0;

contract test{
    function fun1() external{}

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

  编译器会为公共状态变量提供一个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 这一词来对函数的严格性进行描述,在函数类型转换时对严格行有一定的要求,高严格性函数可以被转化为低严格性函数:

  真是令人头秃!

toutu