在 Solidity 中,view、pure 和 payable 是三种重要的函数修饰符,它们定义了函数的行为特性和限制条件。
1. View 修饰符
定义:声明函数不会修改合约的状态变量,但可以读取状态。
特点:
- 可以读取状态变量(storage)
- 不能修改状态变量
- 不能发送 ETH
- 不消耗 Gas(当被外部调用时)
soliditycontract ViewExample { uint256 public storedData = 100; // view 函数可以读取状态 function getData() public view returns (uint256) { return storedData; // 读取状态变量 } // 错误:view 函数不能修改状态 function setData(uint256 _data) public view { // storedData = _data; // 编译错误! } }
2. Pure 修饰符
定义:声明函数既不读取也不修改合约状态,仅依赖于输入参数。
特点:
- 不能读取状态变量
- 不能修改状态变量
- 不能访问
msg.sender、msg.value等全局变量 - 不消耗 Gas(当被外部调用时)
soliditycontract PureExample { uint256 public constant VALUE = 100; // pure 函数只依赖输入参数 function add(uint256 a, uint256 b) public pure returns (uint256) { return a + b; } // pure 函数可以读取常量 function getConstant() public pure returns (uint256) { return VALUE; // 常量不算状态读取 } // 错误:pure 函数不能读取状态变量 function getData() public pure returns (uint256) { // return storedData; // 编译错误! } }
3. Payable 修饰符
定义:允许函数接收 ETH(以太币)。
特点:
- 可以接收 ETH 转账
- 可以读取和修改状态(默认行为)
- 可以访问
msg.value获取转账金额 - 消耗 Gas
soliditycontract PayableExample { mapping(address => uint256) public balances; // payable 函数可以接收 ETH function deposit() public payable { require(msg.value > 0, "Must send ETH"); balances[msg.sender] += msg.value; } // 查询合约余额 function getBalance() public view returns (uint256) { return address(this).balance; } // 提取 ETH function withdraw(uint256 amount) public { require(balances[msg.sender] >= amount, "Insufficient balance"); balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); } }
修饰符对比表
| 特性 | view | pure | payable |
|---|---|---|---|
| 读取状态 | ✅ | ❌ | ✅ |
| 修改状态 | ❌ | ❌ | ✅ |
| 接收 ETH | ❌ | ❌ | ✅ |
| 访问 msg.value | ❌ | ❌ | ✅ |
| Gas 消耗(外部调用) | 无 | 无 | 有 |
| 适用场景 | 查询数据 | 纯计算 | 资金操作 |
组合使用
某些修饰符可以组合使用:
soliditycontract CombinedExample { // payable + view 不能组合,因为 view 不消耗 Gas,而 payable 需要处理转账 // 可以定义接收 ETH 的函数 receive() external payable {} fallback() external payable {} // 计算函数使用 pure function calculateFee(uint256 amount) public pure returns (uint256) { return amount * 5 / 100; // 5% 手续费 } // 查询函数使用 view function getContractBalance() public view returns (uint256) { return address(this).balance; } }
实际应用示例
soliditycontract Bank { mapping(address => uint256) private balances; uint256 public totalDeposits; // payable:接收存款 function deposit() public payable { balances[msg.sender] += msg.value; totalDeposits += msg.value; } // view:查询余额 function getBalance(address user) public view returns (uint256) { return balances[user]; } // pure:计算利息 function calculateInterest(uint256 principal, uint256 rate) public pure returns (uint256) { return principal * rate / 100; } // payable + 其他操作 function withdraw() public payable { uint256 amount = balances[msg.sender]; require(amount > 0, "No balance"); balances[msg.sender] = 0; totalDeposits -= amount; payable(msg.sender).transfer(amount); } }
最佳实践
- 优先使用 pure:如果函数不需要读取状态,使用 pure 可以节省 Gas
- 明确使用 view:对于只读操作,明确标记 view 提高代码可读性
- 谨慎使用 payable:确保 payable 函数有适当的访问控制和金额验证
- 避免滥用:不要为了 Gas 优化而错误地使用这些修饰符,可能导致安全问题