# Learn Solidity Series 8: contract event **Published by:** [bit1](https://paragraph.com/@bit1/) **Published on:** 2022-03-18 **URL:** https://paragraph.com/@bit1/learn-solidity-series-8-contract-event ## Content 1. create contract instance创建合约的语法:new 合约名(); 示例: contract A {} contract B { function test() public { A a = new A(); } } 创建合约实例的时候,默认会调用构造函数,并执行构造函数中的代码。如果没有定义构造函数,EVM编译器会自动生成一个默认的构造函数。如果构造函数指定了参数,那么创建合约实例时候需要传入相同数量的实参。contract A { int num; constructor(int _num) public { num = _num; } } contract B { function test() public { A a = new A(10); } } 注意:合约构造函数是可选的。solidity不支持构造函数的重载,也就是一个合约最多只有一个构造函数。2. contract inherit一个合约可以继承另外一个合约,也可以同时继承多个合约contract A {} contract B is A {} 如果同时继承了多个父合约,最远的派生函数会被调用。 contract C is A, B {} contract SimpleContract { function test() public returns(uint) { C c = new C(); c.a(); // 20 c.f(); // 20 } } 上面合约C同时继承了A和B,因为B合约比A合约距离C合约远,所以在test函数中调用父合约状态变量和函数,实际调用的是B合约。 父合约的函数可以被派生合约中具有相同名称、相同参数的函数重载。如果派生合约的重载函数的返回值类型或数量不相同,会导致错误。 如果父合约构造函数带参数,那么可以在派生合约声明时候指定参数,或者也可以在构造函数位置以修饰器方式提供。contract A { int num; constructor(int _num) public { num = _num; } } contract B is A {} contract B is A(10) {} contract B is A { constructor() public A(10) {} } 3. abstract contract如果一个派生合约没有指定所有父合约的构造函数参数,那么该合约是一个抽象合约。 contract A { uint a; constructor(uint _a) internal { a = _a; } } contract B is A {} contract SimpleContract { function test() public { //B a = new B(); } } 因为合约B继承合约A时候,没有指定构造函数参数,因此合约B是一个抽象合约。抽象合约不能够实例化,因此上面test函数中注释代码会报错。 另外,如果一个合约包含未实现的函数,那么该合约也是一个抽象合约。contract A { function test() public; } 4. interface接口主要用于规范合约的实现,其定义格式为: 定义接口使用interface关键字。接口里面只能够声明函数,不能定义状态变量和构造函数,也不能够对函数提供实现。interface InterfaceA { function f() external; } 上面接口函数f()必须要使用external修饰符。与Java不同,solidity接口之间无法继承。一个合约可以继承多个接口。interface InterfaceA {} interface InterfaceB {} interface Impl is InterfaceA, InterfaceB {} 如果一个合约没有实现接口里面的所有函数,那么该合约是一个抽象合约。 👉抽象合约与接口的相同点和不同点? 1)抽象合约可以定义状态变量和构造函数,而接口不可以; 2)抽象合约中的函数可以不实现,也可以实现,而接口中只能包含未实现的函数; 3)抽象合约中的函数可以使用任意修饰符,但接口中函数只能使用external修饰符; 4)抽象合约可以继承其他合约或接口,而接口不能; 5)抽象合约和接口都不能实例化; 6)抽象合约和接口都可以声明结构体和枚举;5. lib合约库合约不同于一般合约,它更像Java里的工具类,负责为其他合约提供有用的工具函数。 定义语法:library 库合约 {} 库合约可以定义结构体、枚举,也可以声明状态变量,但是状态变量必须是常量。 像一般函数调用一样,库合约中的成员可以通过库合约.成员方式访问。library Utils { function sum(uint a, uint b) public pure returns(uint) { return a + b; } } library SimpleContract { function test() public pure { uint result = Utils.sum(10, 20); } } 如果在合约中包含了库合约,那么部署合约时候,EVM会自动将库合约代码包含进来,无需额外部署库合约。 除了上面调用方式以外,还可以通过using...for语法访问库合约里面的函数。该语法运行将库合约中的函数附加到任意类型上,从而实现类似原生函数调用的效果。library Utils { function get(int[] storage self, uint index) internal view returns(int) { require(index >= 0); return self[index]; } function get(mapping(address => uint128) storage self) internal view returns(uint128) { require(msg.sender > address(0)); return self[msg.sender]; } } contract SimpleContract { int[] ids; mapping(address => uint128) balances; using Utils for int[]; using Utils for mapping(address => uint128); function test() public view { int id = ids.get(0); uint128 bal = balances.get(); } } 上面库合约定义了两个重载方法。当使用using..for语法访问库合约时候,第一个参数为对应类型的状态变量。 上面代码通过using...for语法,将库合约绑定到uint[]和mapping(address => uint128)类型。因此,该合约中所有uint[]和mapping(address => uint128)类型的变量都可以库函数,这时候库函数的第一个参数引用了调用该函数的变量。6.Event事件使得开发者可以访问EVM的日志系统,帮助开发者了解智能合约运行过程的状态信息。开发者可以在dapp中监听solidity事件,当智能合约中触发了某个事件,EVM的日志系统会反过来调用dapp中定义的事件回调函数,从而在回调函数中将日志信息打印出来。 记录在区块链上的日志信息可以被外部检索出来。solidity规定一个事件里面最多有3个参数可以设置为indexed,表示用户可以通过该参数对搜索结果进行过滤。所有非indexed参数将保存在日志的数据部分,而indexed参数不会被保存。contract SimpleContract { event LogSave( address indexed _addr, uint _value ); function saveMoney() public payable { if (msg.value <= 100 wei) { emit LogSave(msg.sender, msg.value); } } } 上面合约定义了一个LogSave事件,其中第一个参数是可索引参数。在saveMoney函数中,判断如果msg.value小于等于100 wei,则触发LogSave事件。 ## Publication Information - [bit1](https://paragraph.com/@bit1/): Publication homepage - [All Posts](https://paragraph.com/@bit1/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@bit1): Subscribe to updates