深入理解Solidity 三

Solidity函数声明和类型

  •  函数的值类型有两类:内部(internal)类型和外部(external)类型
  • 内部函数只可以在当前合约内部被调用(即在当前代码块内,包括内部库函数和继承函数),因为他们不能在当前合约的上下文中的外部被执行。调用一个内部函数是通过跳转到它的入口标签来实现的,就像在当前合约的内部调用一个函数
  • 外部函数是由一个地址和一个函数的签名组成,可以通过外部函数调用传递或者返回
  • 调用内部函数,直接使用名字f
  • 调用外部函数:this.f(当前合约),a.f(外部合约)
  • 除了使用外部函数调用外,还可以使用继承机制

例子(外部函数调用)

contract C{
    uint a;
    function f() public{}
}
contract D{
    function g() public{
        C c = new C();
        c.f();
    }
}

例子(继承机制)

  • 可以使用internal函数/public函数,不可以使用private函数
  • D继承C合约,只会把交叉的函数编译上传
contract C{
    uint a;
    function f() public{}
}
contract D is C{
    function g() public{
        f();
    }
}

函数的可见性

  • 函数的可见性可以指定为external、public、internal或者private;对于状态变量,不可以设置为external,默认是internal
  • external:外部函数作为合约接口的一部分,意味着我们可以从其他合约和交易中调用。一个外部函数f不可以从内部调用(即f不起作用,但是this.f可以)。当接收到大量的数据的时候,外部函数有时候会更有效率
  • public:public函数是合约接口的一部分,相当于定义了一个view类型的可以返回参数的函数,可以在内部或者通过消息调用。对于public状态变量,会自动生成一个getter函数

例子

uint public a;//将a声明为public,就相当于为a定义了如下的函数形式
function a() public view returns (uint){
    return a;
}
  • internal:这些函数和状态变量只能是内部访问(即从当前合约内部或者从其他派生的合约访问),不可以使用this
  • private:private函数和状态变量仅在当前定义他们的合约中使用,并且不能被派生合约使用。

函数状态可变性

  • payable:允许从消息调用中接收以太币Ether
  • constant:和view相同,一般只修饰状态变量,不允许赋值(除了初始化之外)
 constructor()public payable{}//创建合约的同时需要往合约上面转钱
  • pure:纯函数,不允许修改或者访问状态
  • view:不允许修改状态

函数状态可变性

  • 修改状态变量
  • 产生事件
  • 创建其他合约
  • 使用selfdestruct(自杀/自毁)
  • 通过调用发送以太币
  • 调用任何没有标记为view或者pure的函数
  • 使用低级调用
  • 使用包含特定操作吗的内联汇编

以下是被认为从状态中进行读取

  • 读取状态变量
  • 访问this.balance或者<address>.balance
  • 访问block、tx、msg中任意成员(除了msg.sig和msg.data之外)
  • 调用任何未标记为pure的函数
  • 使用包含某些操作码的内联汇编

函数修饰器 modifier

  • 使用修饰器modifier可以轻松改变函数的行为。例如,他们可以在执行函数之前自动检查某个条件。修饰器modifier是合约的可继承属性,并可能被派生合约覆盖
  • 如果同一个函数有多个修饰器modifier,他们之间可以使用空格隔开,修饰器modifier会依次检查执行。

代码

contract Purchase{
    address public seller;
    constructor() public{
        seller = msg.sender;
    }
    modifier onlySeller(){
        require(msg.sender == seller,"Only seller");
        _;
    }
    function f() public view onlySeller returns(uint){
        return 200;
    }
}

效果展示

  • msg.sender是合约的部署者,只有合约的部署者调用f函数,返回200,剩余的人会返回错误信息,only seller
  • require(msg.sender == seller,"Only seller");逗号之前是检查条件,之后是返回报错信息,检查条件在代码流程之前执行
  • _;将代码流程嵌套到这里,指代检查条件执行之后,执行代码的逻辑。(函数体)

回退函数(fallback)

  • 回退函数(fallback function)是合约中的特殊函数;没有名字,不能有参数也不能有返回值
  • 如果在一个到合约的调用中,没有其他函数与给定的函数标示符匹配(或者没有提供调用函数),你们这个函数(fallbacj函数)会被执行
  • 每当合约收到以太币(没有任何数据),回退函数就会执行。此外,为了接收以太币,fallback函数必须标记为payable。如果不存在这样的函数,则合约不能通过常规交易接收以太币
  • 在上下文中只有很少的gas可以用来完成回退函数的调用,所以使fallback函数的调用尽量廉价很重要

代码

pragma solidity >0.4.99  <0.6.0;
contract Sink{
    function() external payable{}
}
contract Test{
    function() external {x=1;}
    uint x;
}

contract Caller{
    function calllTest(Test test)public returns(bool){
        (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
        require(success);
        address payable testPayable = address(uint160(address(test)));
        return testPayable.send(2 ether);
    }
}

注解

  • 调用合约中不存在的函数和转钱的时候会调用回退函数
  • 回退函数适用在合约5.0版本以上

事件(event)

  • 事件是以太坊EVM提供的一种日志基础设施。事件可以用来做操作记录,存储为日志。也可以用来实现一些交互功能,比如通知UI,返回函数的调用结果
  • 当定义的事件被触发时,可以将事件存储到EVM的交易日志中,日志是区块链中的一种特殊的数据结构;日志和合约相互关联,与合约的存储合并存入到区块链条中,只要某个区块可以访问,其相关的日志就可以访问,但是在合约中是不可以之间访问日志和事件数据
  • 可以通过日志实现简单支付验证SPV,如果一个外部实体提供了一个带有这种证明的合约,它可以检查日志是否真实存在于区块链中

异常处理

  • 适用状态恢复异常来处理异常。这样的异常将会撤销对于当前的调用(及其所有的子调用)中的状态所做的所有的更改,并且向调用者返回错误
  • 函数的assert和require用于判断条件,并且在不满足条件的时候抛出异常
  • assert()一般只应用于测试内部的错误,并且检查异常
  • require()应用于确保满足有效的条件(如输入或合约的状态变量),或者验证调用外部合约的返回值
  • revert()用于抛出异常,它可以标记一个错误并且将当前调用回退

单位

  • 以太币单位之间的换算就是在数字后边加上wei、finney、szabo或者ether来实现,如果没有单位,缺省为wei

时间

  • 秒是缺省单位,在时间单位之间,数字后面带有seconds、minutes、hours、days、weeks和years的可以进行换算 但是这些后缀不可以直接用在变量的后边,如果需要使用到时间单位(例如days)来将输入变量转换为时间,可以使用如下方式
function f(uint start,uint daysAfter)public{
    if (now >= start + daysAfter * 1 days)
}

猜你喜欢

转载自blog.csdn.net/CHYabc123456hh/article/details/107017762