继续上一篇的内容,这次来看看合约的更多语法以及如何引入其他合约进行交互。
构造函数
和其他面向对象语言一样,Solidity 中也有构造函数。
constructor({[参数类型] [参数名], ...}) {}
例如:
address public immutable contractOwner;
constructor() {
contractOwner = msg.sender;
}
这样就可以在这个合约对象被实例化之后给 contractOwner 指定一个值,这里的值就是创建此合约的发送者地址。
合约交互
在这之前,先用之前学过的知识对 SimpleStorage 改造一下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct People {
string name;
address peopleAddress;
uint256 favoriteNumber;
}
contract SimpleStorage {
address public immutable contractOwner;
constructor() {
contractOwner = msg.sender;
}
mapping(string => People) public nameToPeople;
function addPeople(string calldata name) public {
nameToPeople[name] = People(name, msg.sender, 0);
}
function getPeople(string calldata name) public view returns (People memory) {
return nameToPeople[name];
}
function setFavoriteNumber(string calldata name, uint256 number) public {
People storage p = nameToPeople[name];
p.favoriteNumber = number;
}
function removePeople(string calldata name) public {
nameToPeople[name] = People("", 0x0000000000000000000000000000000000000000, 0);
}
}
这里定义了一个 People 名字对 People 对象的映射,下面是对这个映射的简单增删改查函数。
注意这个 People 结构被放在了合约外部,因为其他合约也可能用到这个结构。
引入合约
使用 import
关键字引入其他合约,例如 import "./path/to/contract/xxx.sol"
接下来创建一个新文件:SimpleStorageFactory.sol,然后引入刚才的 SimpleStorage 合约。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./SimpleStorage.sol";
contract SimpleStorageFactory {
SimpleStorage[] private storages;
}
实例化合约
使用 new
关键字来实例化一个合约,和 Java 是一样的。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./SimpleStorage.sol";
contract SimpleStorageFactory {
SimpleStorage[] private storages;
function createNewStorage() public {
storages.push(new SimpleStorage());
}
}
这样就完成了一个创建合约的函数,这个合约被保存在 storages 数组内。
与合约交互
现在 storages 储存了许多合约,下面就需要对不同的合约进行不同的操作了。
和其他面向对象语言一样,直接 对象.函数()
调用对应方法即可。
下面来完成这个 SimpleStorageFactory 合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./SimpleStorage.sol";
contract SimpleStorageFactory {
SimpleStorage[] private storages;
function createNewStorage() public {
storages.push(new SimpleStorage());
}
function addPeople(uint index, string calldata name, uint256 defaultNumber) public {
SimpleStorage s = storages[index];
s.addPeople(name);
if (defaultNumber != 0) {
s.setFavoriteNumber(name, defaultNumber);
}
}
function getPeople(uint index, string calldata name) public view returns (People memory) {
return storages[index].getPeople(name);
}
function removePeople(uint index, string calldata name) public {
SimpleStorage s = storages[index];
s.removePeople(name);
}
}
继承与重写
继承和重写是面向对象中的两个很重要的概念,Solidity 也同样支持这样的操作。
继承
在 Solidity 中,使用 is
关键字来继承其他合约。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./SimpleStorageExt.sol";
contract ExtraStorage is SimpleStorage {
}
这样就完成了 ExtraStorage 对 SimpleStorage 的继承。
重写
一般情况下,父合约的函数是不允许被子合约重写的。
contract ExtraStorage is SimpleStorage {
function addPeople(string calldata name) public {
nameToPeople[name] = People(name, msg.sender, 0);
}
}
这一段代码编译无法通过,错误信息:Overriding function is missing “override” specifier.solidity(9456)
在 Solidity 中,用 virtual
关键字将某个函数标记为可重载,子合约可以用 override
关键字进行重写。
现在回到 SimpleStorage 中,找到这个函数,并且加上 virtual
关键字:
contract SimpleStorage {
// ...
function addPeople(string calldata name) public virtual {
nameToPeople[name] = People(name, msg.sender, 0);
}
// ...
}
现在再给子合约的对应函数加上 override
关键字即可:
contract ExtraStorage is SimpleStorage {
function addPeople(string calldata name) public override {
nameToPeople[name] = People(name, msg.sender, 0);
}
}
字符串拼接
在 Solidity 中,字符串拼接不能直接使用 +
连接,而是需要自己实现一个方法来完成这个操作。
字符串的本质就是一串字节,也就是字节数组,只要把两个字节数组拼接成一个新的字节数组,再重新转换成 string 就好了。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./SimpleStorageExt.sol";
contract ExtraStorage is SimpleStorage {
function addPeople(string calldata name) public override {
string memory tName = combine("Prefix_", name);
nameToPeople[name] = People(tName, msg.sender, 0);
}
function combine(string memory s1, string memory s2) public pure returns (string memory) {
bytes memory s1bytes = bytes(s1);
bytes memory s2bytes = bytes(s2);
bytes memory result = new bytes(s1bytes.length + s2bytes.length);
uint256 k = 0;
for (uint256 i = 0; i < s1bytes.length; i++) {
result[k++] = s1bytes[i];
}
for (uint256 i = 0; i < s2bytes.length; i++) {
result[k++] = s2bytes[i];
}
return string(result);
}
}
接下来继续完善一下上面的重写函数,这个子合约需要在添加 People 时给 People 的名字加上一个前缀。
contract ExtraStorage is SimpleStorage {
function addPeople(string calldata name) public override {
string memory tName = combine("Prefix_", name);
nameToPeople[tName] = People(tName, msg.sender, 0);
}
// ...
}
结束语
本期介绍了合约的构造函数与实例化以及继承重写,还有字符串拼接的方法。
下一期将会再次对合约的其他内容进行补充,如有错误欢迎指出。