Solidity-函数、函数修改器、事件(四)

前言

函数、函数修改器、事件。


一、函数修改器

函数修改器类似于装饰器,目的是在在不改动原函数的情况下修改函数的行为,减少代码量。

函数修改器(modifier)是不可以重载的。修改器的定义使用modifier,而不是Function的。

modifier onlyowner() {
        require(
            msg.sender == owner,
            "Only owner."
        );
        _;
}

onlyowner修改器,被修饰的函数会在“_”处执行。

函数修改器可以带参数。

二、事件 Event

 事件是能方便地调用以太坊虚拟机日志功能的接口。

事件的定义:event Transfer(address indexed from,address indexed to,uint amount);

事件的触发:emit Transfer(from,to,amount);

indexed是让evm将数据放入topic,不加则放入data,放入topic基础费用375,data每字节8.

事件是调用evm日志功能的接口,所以对应着evm字节码log0,log1...log4.其中log0不存topic(32字节),仅存数据。其中topic[0]是事件的摘要即keccak256() hash,这样就决定了最多可以有3个indexed(在使用log4的情况下)。

事件在合约中可被继承。当他们被调用时,会使参数被存储到交易的日志中 —— 一种区块链中的特殊数据结构。 这些日志与地址相关联,被并入区块链中,只要区块可以访问就一直存在,日志和事件在合约内不可直接被访问(甚至是创建日志的合约也不能访问)。

如果外部实体需要该日志实际上存在于区块链中的证明,可以请求日志的Merkle证明. 但由于合约中仅能访问最近的 256 个区块哈希,所以需要提供区块头信息。

除非用 anonymous 声明事件,否则事件签名的哈希值是一个topic 。 同时也意味着对于匿名事件无法通过名字来过滤,仅能按合约地址过滤。 匿名事件的优势是他们部署和调用的成本更低。它也允许你声明 4 个索引参与而不是 3 个。

event Transfer(address indexed from,address indexed to,uint amount) anonymous;

事件成员

event.selector: 对于非匿名事件,这是一个 bytes32 值,包含事件签名的 keccak256 哈希值,在默认主题中使用。

三、函数 

 可以在合约内部和外部定义函数。

函数可以在合约外部定义是从 0.7.0 之后才开始支持的。

 合约之外的函数(也称为“自由函数”)始终具有隐式的 internal 可见性。 它们的代码包含在所有调用它们合约中,类似于内部库函数。

 在合约之外定义的函数仍然在合约的上下文内执行。 他们仍然可以调用其他合约,将其发送以太币或销毁调用它们的合约等其他事情。 与在合约中定义的函数的主要区别为:自由函数不能直接访问存储变量this 、存储和不在他们的作用域范围内函数。

1、返回值

returns(type aa(可选)) 写了变量的话,可以通过赋值返回函数返回值,不用return

return(aa)

2、状态可变性

 View 视图函数

可以将函数声明为 view 类型,这种情况下要保证不修改状态。

  • 下面的语句被认为是修改状态:
  • 修改状态变量。
  • 产生事件
  • 创建其他合约
  • 使用 selfdestruct
  • 通过调用发送以太币。
  • 调用任何没有标记为 view 或者 pure 的函数。
  • 使用低级调用。
  • 使用包含特定操作码的内联汇编。

 constant 之前是 view 的别名,不过在0.5.0之后移除了。

 Getter 方法自动被标记为 view

在0.5.0 版本之前, 编译器没有对 view 函数使用 STATICCALL 操作码。 这样通过使用无效的显式类型转换会启用视图函数中的状态修改。 通过对 view 函数使用 STATICCALL , 可以防止在 EVM 级别上对状态进行修改。

 Pure 纯函数

 函数可以声明为pure,在这种情况下,承诺不读取也不修改状态变量。

如果编译器的 EVM 编译目标设置为 Byzantium 或之后的版本 (默认), 则使用操作码 STATICCALL , 这并不保证状态未被读取, 但至少不被修改。 

以下被认为是读取状态:

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

不可能在 EVM 级别阻止函数读取状态, 只能阻止它们写入状态 (即只能在 EVM 级别强制执行 view , 而 pure 不能强制)。 

在0.4.17版本之前,编译器不会强制 pure 函数不读取状态。它是一个编译时类型检查, 可以避免在合约类型之间进行无效的显式转换, 因为编译器可以验证合约类型没有状态更改操作, 但它不会在运行时能检查调用实际的类型。 

3、特别的函数

receive

 一个合约最多有一个 receive 函数, 声明函数为:receive() external payable { ... }

不需要 function 关键字,也没有参数和返回值并且必须是external可见性和payable修饰. 它可以是 virtual 的,可以被重载也可以有modifier 。

 在对合约没有任何附加数据调用(通常是对合约转账)是会执行receive函数。例如 通过 .send() or .transfer() 如果 receive 函数不存在,但是有payable的fallback函数,那么在进行纯以太转账时,fallback函数会调用。

一个没有receive函数的合约,可以作为coinbase交易 又名 矿工区块回报 的接收者或者作为selfdestruct的目标来接收以太币。

一个合约不能对这种以太币转移做出反应,因此也不能拒绝它们。这是 EVM 在设计时就决定好的,而且 Solidity 无法绕过这个问题。这也意味着address(this).balance可以高于合约中实现的一些手工记帐的总和(例如在receive 函数中更新的累加器记帐)。

 Fallback

 合约可以最多有一个回退函数。函数声明为: fallback() external [payable]00..fallback (bytes calldata input) external [payable] returns (bytes memory output).

 不需要 function 关键字,也没有参数和返回值并且必须是external可见性和payable修饰. 它可以是 virtual 的,可以被重载也可以有modifier 。

如果在一个对合约调用中,没有其他函数与给定的函数标识符匹配fallback会被调用. 或者在没有 receive函数时,而没有提供附加数据对合约调用,那么fallback 函数会被执行。

 4、函数重载

合约可以具有多个不同参数的同名函数,称为“重载”(overloading),这也适用于继承函数。

重载函数也存在于外部接口中。如果两个外部可见函数仅区别于 Solidity 内的类型而不是它们的外部类型则会导致错误。例如:contractType和address。

你可能感兴趣的:(区块链)