solidity高级语法

高级语法

1. 自动推导 var

强烈不建议使用!!

为了方便,并不总是需要明确指定一个变量的类型,编译器会通过第一个向这个对象赋予的值的类型来进行推断

uint24 x = 0x123;
var y = x;
  • 由var引发的血案…

    需要特别注意:由于类型推断是根据第一个变量进行的赋值。所以下面的代码将是一个无限循环,因为一个uint8的i的将小于2000。

for (var i = 0; i < 2000; i++)
{
    //uint8 -> 255
	//无限循环
}
pragma solidity ^0.4.25;

contract Test{
    function Hi() view returns (uint, uint){
        uint count = 0;
        var i = 0;
        for (; i < 257; i++) {
            count++;
            if(count >= 260){
                break;
            }
        }
        return (count, i);
    }
}
/*
结果:
0: uint256: 260
1: uint256: 3

分析:当 循环到 i =255 时,count = 256,再往后 i 又从0开始了!
i   ,  count
255 ,  256
0   ,  257
1   ,  258
2   ,  259
3   ,  260
*/

2.全局函数/变量

2.1 区块和交易的属性

函数 含义
block.blockhash(uint blockNumber) 哈希值(byte32)
block.coinbase (address) 当前块矿工的地址。
block.difficulty (uint)当前块的难度
block.gaslimit (uint)当前块的gaslimit
block.number (uint)当前区块的块号
block.timestamp (uint)当前块的时间戳
msg.data (bytes)完整的调用数据(calldata)
msg.gas (uint)当前还剩的gas
msg.sender (address)当前调用发起人的地址
msg.sig (bytes4)调用数据的前四个字节(函数标识符)
msg.value (uint)这个消息所附带的货币量,单位为wei
now (uint)当前块的时间戳 等同于block.timestamp
tx.gasprice (uint) 交易的gas价格
tx.origin (address)交易的发送者(完整的调用链)
  • 示例:
pragma solidity ^0.4.25;

contract Test {
    bytes32 public blockhash;
    address public coinbase;
    uint public difficulty;
    uint public gaslimit;
    uint public blockNum;
    uint public timestamp;
    bytes public calldata;
    uint public gas;
    address public sender;
    bytes4 public sig;
    uint public msgValue;
    uint public now;
    uint public gasPrice;
    address public txOrigin;
    
    function letgo (){
        //给定区块号的哈希值,只支持最近256个区块,且不包含当前区块
        blockhash = block.blockhash(block.number - 1);
        coinbase = block.coinbase ;//当前块矿工的地址。
        difficulty = block.difficulty;//当前块的难度。
        gaslimit = block.gaslimit;// (uint)当前块的gaslimit。
        blockNum = block.number;// (uint)当前区块的块号。
        timestamp = block.timestamp;// (uint)当前块的时间戳。
        calldata = msg.data;// (bytes)完整的调用数据(calldata)。
        gas = msg.gas;// (uint)当前还剩的gas。
        sender = msg.sender; // (address)当前调用发起人的地址。
        sig = msg.sig;// (bytes4)调用数据的前四个字节(函数标识符)。
        msgValue = msg.value;// (uint)这个消息所附带的货币量,单位为wei。
        now = now;// (uint)当前块的时间戳,等同于block.timestamp
        gasPrice = tx.gasprice;// (uint) 交易的gas价格。
        txOrigin = tx.origin;// (address)交易的发送者(完整的调用链)  
    }
}

2.2 货币单位

  • 一个字面量的数字,可以使用后缀weifinneyszaboether来在不同面额中转换。
  • 不含任何后缀的默认单位是wei。如1 ether == 1000 finney的结果是true
pragma solidity ^0.4.25;

contract EthUnit{
    uint  a = 1 ether;
    uint  b = 10 ** 18 wei;
    uint  c = 1000 finney;
    uint  d = 1000000 szabo;
    
    function f1() constant public returns (bool){
        return a == b;
    }
    
    function f2() constant public returns (bool){
        return a == c;
    }
    
    function f3() constant public returns (bool){
        return a == d;
    }
    
    function f4() constant public returns (bool){
        return 1 ether == 100 wei;
    }
}

2.3 时间单位

  • seconds,minutes,hours,days,weeks,years均可做为后缀,默认是seconds为单位。
  • 1 = 1 seconds
  • 1 minutes = 60 seconds
  • 1 hours = 60 minutes
  • 1 days = 24 hours
  • 1 weeks = 7 days
  • 1 years = 365 days
pragma solidity ^0.4.25;

contract TimeUnit{

    function f1() pure public returns (bool) {
        return 1 == 1 seconds;
    }
    
    function f2() pure public returns (bool) {
        return 1 minutes == 60 seconds;
    }
    
    function f3() pure public returns (bool) {
        return 1 hours == 60 minutes;
    }
    
    function f4() pure public returns (bool) {
        return 1 days == 24 hours;
    }
    
    function f5() pure public returns (bool) {
        return 1 weeks == 7 days;
    }
    
    function f6() pure public returns (bool) {
        return 1 years == 365 days;
    }
}

3.事件(Event)

  • 可以用来记录日志
pragma solidity ^0.4.0;

contract ClientReceipt {
    //定义,注意,需要加分号,相当于一句语句,与struct和enum不同。
    event Deposit(
        address indexed _from,
        uint indexed _id,
        uint _value
    );

    function deposit(uint _id) {
        //使用
        Deposit(msg.sender, _id, msg.value);
    }
}

在这里插入图片描述

4.访问函数

  • 编译器为自动为所有的public的状态变量创建访问函数。下面的合约例子中,编译器会生成一个名叫data的无参,返回值是uint的类型的值data。状态变量的初始化可以在定义时完成。
pragma solidity ^0.4.25;

contract C{
    uint public data = 10;
}

contract D{
    C c = new C();
    
    function getDataUsingAccessor() returns (uint){
        return c.data();
    }
}
  • 访问函数有外部(external)可见性。如果通过内部(internal)的方式访问,比如直接访问,你可以直接把它当一个变量进行使用,但如果使用外部(external)的方式来访问,如通过this.,那么它必须通过函数的方式来调用。
pragma solidity ^0.4.25;

contract viewTest{
    uint public c = 10;
    
    function accessInternal() internal view returns (uint){
        return c;
    }
    //合约内部也能够调用用external修饰的函数, 但是要使用外部调用方式this	
    function accessExternal() view external returns (uint){
        return this.c();
    }
}

5.错误处理

传统方法:采用 throw 和 if … throw 模式==(已过时)==,例如合约中有一些功能,只能被授权为拥有者的地址才能调用

if(msg.sender != owner) { 
	throw; 
}

等价于如下任意一种形式:

if(msg.sender != owner) { 
	revert(); 
} 
assert(msg.sender == owner); 
require(msg.sender == owner);

示例:

pragma solidity ^0.4.21;

contract HasAnOwner {
    address public owner;
    uint public a ;
    
    constructor() public {
        owner = msg.sender;
    }
    
    function useSuperPowers()  public { 
        require(msg.sender == owner);
        a = 11111;
        /*
        if (msg.sender != owner){
            throw;
        }
        */
        
        a = 10;
       // do something only the owner should be allowed to do
    }
}

6.修饰器(modifier)

修改器(Modifiers)可以用来轻易的改变一个函数的行为。比如用于在函数执行前检查某种前置条件。修改器是一种合约属性,可被继承,同时还可被派生的合约重写(override)。下面我们来看一段示例代码:

pragma solidity ^0.4.21;

contract HasAnOwner {
    address public owner;
    uint public a ;
    
    constructor() public {
        owner = msg.sender;
    }
    
    modifier ownerOnly(address addr) {
        require(addr == owner);
        //代码修饰器所修饰函数的代码
        _;
    }
    
    function useSuperPowers() ownerOnly(msg.sender) public { 
    	//require(addr == owner);
        a = 10;
       // do something only the owner should be allowed to do
    }
}

7.元组 (tuple)

元组是一个数据集合,类似于字典但是无法修改数据,使用圆括号包括多种数据类型。

pragma solidity ^0.4.25;

contract Test {
    
    struct Student {
        string name;
        uint age;
        uint score;
        string sex;
    }
    
    //两种赋值方式
    Student public stu1 = Student("lily", 18, 90, "girl");
    Student public stu2 = Student({name:"Jim", age:20, score:80, sex:"boy"});

    Student[] public Students;
    
    function assign() public {
        Students.push(stu1);
        Students.push(stu2);
        
        stu1.name = "Lily";
    }
    
    //1. 返回一个Student结构
    function getLily() public view returns(string, uint, uint, string) {
        Student memory lily = Students[0];
        return (lily.name, lily.age, lily.score, lily.sex);
    }
}

8. 内置数学函数

ripemd160

keccak256

addmod

ecrecover

9.继承

  • 继承关键字:is,语法:contract Son is Father{ }
  • 合约 继承 另一个合约 来 达到复用的目的
    • 继承 父合约的所有成员变量(state var - 状态变量)
    • 继承 父合约的所有方法
  • 继承的本质:代码复制,将 父合约 代码复制到 子合约
pragma solidity ^0.4.25;
//父合约
contract Father{
    string public fatherName = "James";
}
//子合约
contract Son is Father{
    string public sonName = "Jun";
    
    function testFather() public{
        fatherName = "James01";
        sonName = "Jun01";
    }
}
  • super和this关键字
    • super 在合约中指向父合约
    • this 在合约中指向本合约
pragma solidity ^0.4.25;
//父合约
contract Father{
    string public fatherName = "James";
    function printName() public view returns(string){
        return fatherName;
    }
}
//子合约
contract Son is Father{
    string public sonName = "Jun";
    
    function testFather() public{
        this.sonName = "Jun01";
        super.fatherName = "James01";
        super.printName();
    }
}
  • 注意:父子同名的成员都会存在,只是访问方式有变化
pragma solidity ^0.4.25;

contract Father{
    string public fatherName = "James";
    function outName() public view returns(string){
        return fatherName;
    }
}

contract Son is Father{
    string public fatherName = "James2";
    string public sonName = "Jun";
    
    function outName() public view returns(string){
        return super.outName();
    }
}
  • 访问 父合约所有的非私有成员 (包括 internal 的函数 和 成员变量)
contract Father{
  uint public stateVar;//成员变量
  //public 方法
  function aPublicFun() public pure returns(string){
      return "publicFunc invoked";
  }
  function aInternalFun() internal pure returns(string){
      return "internalFunc invoked";
  }
  function aExternalFun() external pure returns(string){
      return "externalFunc invoked";
  }
  function aPrivateFun() private pure returns(string){
      return "privateFunc invoked";
  }
}
// 子合约 继承 父合约
contract Son is Father{
  function testFather() public{
    //不能访问`private`
    //aPrivateFun();

    //访问父类的`public`方法
    aPublicFun();

    //访问父类的状态变量
    stateVar = 10;

    //访问父类的`internal`方法
    aInternalFun();
    //访问父类的'external'方法,需要通过this关键字 
    this.aExternalFun();
  }
    
  // 调用某合约访问权限是external的方法
  function testExternal(Father fObj)public view returns(string){
      return fObj.aExternalFun();
  }
}

10.继承中同名成员

问题:如果父合约 与 子合约 中 有 相同名字 的 成员变量 或 方法的话,会怎么样?
答:不管同名与否,父合约所有成员都会被 “继承” 到 子合约中。只是子合约调用时有些区别。

  • 访问就近原则:子合约中 调用 同名成员 都是子合约中的,如:
pragma solidity ^0.4.25;

contract Father{
    string public fatherName = "James";
}

contract Son is Father{
    string public fatherName = "James2";
    
    function outName() public view returns(string){
        return fatherName; // James2
    }
}
  • 父合约与子合约
  • 最远继承原则:在继承链中,由于继承实现是代码复制。如果出现函数名重写,最终使用的是继承链上哪个合约定义的代码呢?实际执行时,依据的是 最远继承原则(most derived)
pragma solidity ^0.4.0;

contract Base1{
  function data() public view returns(uint){
    return 1;
  }
}

contract Base2{
  function data() public view  returns(uint){
    return 2;
  }
}

contract MostDerived1 is Base1, Base2{ // extends Base2 (most derived)
  function call() public view returns(uint){
    return data(); // Base2.data()
  }
}

contract MostDerived2 is Base2, Base1{ // extends Base1 (most derived)
  function call() public view returns(uint){
    return data(); // Base1.data()
  }
}
  • 可以指定某个父合约
pragma solidity ^0.4.0;
contract owned {
    function owned() { owner = msg.sender; }
    address owner;
}

contract mortal is owned {
    event mortalCalled(string);
    function kill() {
        mortalCalled("mortalCalled");
        if (msg.sender == owner) selfdestruct(owner);
    }
}

contract SpecifyBase is mortal {
    event SpecifyBase(string);
    function kill() {
      SpecifyBase("do own cleanup");
      mortal.kill();
    }
}

11.外部调用

外部调用,后面项目会用到

pragma solidity ^0.4.24;


contract C1 {
    uint public num = 10;
    function setValue(uint a) public {
            num = a;
    }
}

//手动赋值
contract C2 {
    C1 c1;
    function call (address addr) public {
        c1 = C1(addr);
        c1.setValue(100);
        
        //addr.setValue(1000); 不允许
    }
}

//合约自动调用
contract C3 {
    C1 c1;
    
    function C3() {
        address  c1Addr = new C1();
        c1 = C1(c1Addr);  
        
        //或者 直接使用C1来承接亦可
        //c1 = new C1();
    }
    
    function getValue() public constant returns(uint) {
        return c1.num();
    }
    
    //注意constant 的坑!!!!,
    //函数加上constant之后,运行修改状态变量,但是修改不会生效,好坑!!!
    function setValue(uint num) public {
        c1.setValue(num);
    }
}

官方示例

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() public payable returns (uint ret) {
        return 42; 
    }
}

contract Consumer {
    InfoFeed feed;
    function setFeed(address addr) public { 
        feed = InfoFeed(addr); 
    }
    
    function callFeed() public { 
        feed.info.value(10).gas(800)(); 
    }
}

猜你喜欢

转载自blog.csdn.net/www294993741/article/details/83830778