函数修饰符 #

一、view修饰符 #

1.1 基本概念 #

view修饰符表示函数只读取状态,不修改状态。

solidity
contract ViewModifier {
    uint256 public value = 100;
    
    // view函数:只读取状态
    function getValue() public view returns (uint256) {
        return value;
    }
    
    // view函数可以读取多个状态变量
    function getValues() public view returns (uint256, address) {
        return (value, msg.sender);
    }
}

1.2 允许的操作 #

solidity
contract ViewAllowed {
    uint256 public value = 100;
    mapping(address => uint256) public balances;
    
    // ✓ 读取状态变量
    function readStateVariable() public view returns (uint256) {
        return value;
    }
    
    // ✓ 读取mapping
    function readMapping(address user) public view returns (uint256) {
        return balances[user];
    }
    
    // ✓ 读取address(this).balance
    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }
    
    // ✓ 读取block和msg属性
    function getBlockInfo() public view returns (
        uint256 blockNumber,
        uint256 timestamp,
        address sender
    ) {
        return (block.number, block.timestamp, msg.sender);
    }
    
    // ✓ 调用其他view函数
    function callOtherView() public view returns (uint256) {
        return readStateVariable() * 2;
    }
    
    // ✓ 使用表达式
    function calculate() public view returns (uint256) {
        return value * 2 + 10;
    }
}

1.3 禁止的操作 #

solidity
contract ViewForbidden {
    uint256 public value;
    
    // ✗ 修改状态变量
    // function badModifyState() public view {
    //     value = 1;  // 错误
    // }
    
    // ✗ 触发事件
    // event ValueChanged(uint256 newValue);
    // function badEmitEvent() public view {
    //     emit ValueChanged(1);  // 错误
    // }
    
    // ✗ 创建其他合约
    // function badCreateContract() public view {
    //     new OtherContract();  // 错误
    // }
    
    // ✗ 使用selfdestruct
    // function badSelfdestruct() public view {
    //     selfdestruct(payable(msg.sender));  // 错误
    // }
    
    // ✗ 调用非view函数
    // function nonViewFunction() public {}
    // function badCallNonView() public view {
    //     nonViewFunction();  // 错误
    // }
    
    // ✗ 使用低级调用
    // function badLowLevelCall() public view {
    //     address(0).call("");  // 错误
    // }
}

1.4 view函数的Gas消耗 #

solidity
contract ViewGas {
    uint256 public value = 100;
    
    // view函数从外部调用:不消耗Gas(除了交易费)
    // view函数从内部调用:消耗Gas
    
    function viewFunction() public view returns (uint256) {
        return value;
    }
    
    function callViewFromNonView() public returns (uint256) {
        // 从非view函数调用view函数,消耗Gas
        return viewFunction();
    }
}

二、pure修饰符 #

2.1 基本概念 #

pure修饰符表示函数既不读取也不修改状态。

solidity
contract PureModifier {
    // pure函数:不读取也不修改状态
    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a * b;
    }
}

2.2 允许的操作 #

solidity
contract PureAllowed {
    // ✓ 使用函数参数
    function useParams(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    // ✓ 使用局部变量
    function useLocalVariables() public pure returns (uint256) {
        uint256 a = 1;
        uint256 b = 2;
        return a + b;
    }
    
    // ✓ 使用表达式
    function useExpressions(uint256 a) public pure returns (uint256) {
        return a * 2 + 10;
    }
    
    // ✓ 调用其他pure函数
    function callOtherPure(uint256 a, uint256 b) public pure returns (uint256) {
        return add(a, b);
    }
    
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }
    
    // ✓ 使用keccak256等密码学函数
    function hash(string memory data) public pure returns (bytes32) {
        return keccak256(bytes(data));
    }
    
    // ✓ 使用数学运算
    function power(uint256 base, uint256 exponent) public pure returns (uint256) {
        return base ** exponent;
    }
    
    // ✓ 类型转换
    function convert(uint256 a) public pure returns (uint8) {
        return uint8(a);
    }
}

2.3 禁止的操作 #

solidity
contract PureForbidden {
    uint256 public value;
    
    // ✗ 读取状态变量
    // function badReadState() public pure returns (uint256) {
    //     return value;  // 错误
    // }
    
    // ✗ 读取address(this).balance
    // function badReadBalance() public pure returns (uint256) {
    //     return address(this).balance;  // 错误
    // }
    
    // ✗ 读取block和msg属性(部分)
    // function badReadBlock() public pure returns (uint256) {
    //     return block.number;  // 错误
    // }
    
    // ✗ 调用view函数
    // function viewFunction() public view returns (uint256) {
    //     return value;
    // }
    // function badCallView() public pure returns (uint256) {
    //     return viewFunction();  // 错误
    // }
}

2.4 pure vs view #

solidity
contract PureVsView {
    uint256 public value = 100;
    
    // pure:不读取状态
    function pureFunction(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    // view:读取状态
    function viewFunction() public view returns (uint256) {
        return value;
    }
    
    // 选择原则:
    // - 如果函数不需要读取状态,使用pure
    // - 如果函数需要读取状态,使用view
    // - pure比view更省Gas(从外部调用时)
}

三、payable修饰符 #

3.1 基本概念 #

payable修饰符表示函数可以接收ETH。

solidity
contract PayableModifier {
    mapping(address => uint256) public balances;
    
    // payable函数:可以接收ETH
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }
    
    // 非payable函数:不能接收ETH
    function noPayable() public {
        // 如果发送ETH会revert
    }
}

3.2 使用msg.value #

solidity
contract MsgValue {
    event Deposit(address indexed from, uint256 amount);
    
    // 检查发送的ETH数量
    function deposit() public payable {
        require(msg.value > 0, "Must send ETH");
        emit Deposit(msg.sender, msg.value);
    }
    
    // 要求最小ETH数量
    function depositMin(uint256 minAmount) public payable {
        require(msg.value >= minAmount, "Insufficient ETH");
        emit Deposit(msg.sender, msg.value);
    }
    
    // 返回多余的ETH
    function depositWithRefund(uint256 requiredAmount) public payable {
        require(msg.value >= requiredAmount, "Insufficient ETH");
        
        if (msg.value > requiredAmount) {
            payable(msg.sender).transfer(msg.value - requiredAmount);
        }
        
        emit Deposit(msg.sender, requiredAmount);
    }
}

3.3 payable地址 #

solidity
contract PayableAddress {
    address public owner;
    address payable public treasury;
    
    constructor() {
        owner = msg.sender;
        treasury = payable(msg.sender);
    }
    
    // 发送ETH到指定地址
    function sendToTreasury() public payable {
        treasury.transfer(msg.value);
    }
    
    // 使用call发送ETH
    function sendWithCall(address payable to) public payable {
        (bool success, ) = to.call{value: msg.value}("");
        require(success, "Transfer failed");
    }
    
    // 提取合约余额
    function withdraw() public {
        require(msg.sender == owner, "Not owner");
        payable(owner).transfer(address(this).balance);
    }
}

3.4 payable与view/pure #

solidity
contract PayableViewPure {
    // payable不能与view/pure一起使用
    // 因为接收ETH本身就是状态修改
    
    // ✗ 错误:payable view
    // function badPayableView() public payable view {}
    
    // ✗ 错误:payable pure
    // function badPayablePure() public payable pure {}
    
    // ✓ 正确:payable可以单独使用
    function goodPayable() public payable {}
}

四、修饰符组合 #

4.1 可见性与状态修饰符 #

solidity
contract ModifierCombination {
    uint256 private value;
    
    // public + view
    function publicView() public view returns (uint256) {
        return value;
    }
    
    // public + pure
    function publicPure(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    // public + payable
    function publicPayable() public payable {}
    
    // private + view
    function privateView() private view returns (uint256) {
        return value;
    }
    
    // internal + pure
    function internalPure(uint256 a) internal pure returns (uint256) {
        return a * 2;
    }
    
    // external + view
    function externalView() external view returns (uint256) {
        return value;
    }
}

4.2 修饰符顺序 #

solidity
contract ModifierOrder {
    // 推荐顺序:可见性 -> 状态修饰符
    function goodOrder() public view returns (uint256) {
        return 1;
    }
    
    // 也可以:状态修饰符 -> 可见性
    // 但不推荐
    function alternativeOrder() view public returns (uint256) {
        return 1;
    }
}

五、实际应用示例 #

5.1 众筹合约 #

solidity
contract Crowdfunding {
    address public owner;
    uint256 public goal;
    uint256 public deadline;
    uint256 public totalRaised;
    bool public claimed;
    
    mapping(address => uint256) public contributions;
    
    event Contribution(address indexed contributor, uint256 amount);
    event GoalReached(uint256 totalRaised);
    event FundsWithdrawn(address indexed owner, uint256 amount);
    
    constructor(uint256 _goal, uint256 _durationInDays) {
        owner = msg.sender;
        goal = _goal;
        deadline = block.timestamp + _durationInDays * 1 days;
    }
    
    // payable:接收ETH
    function contribute() public payable {
        require(block.timestamp < deadline, "Campaign ended");
        require(msg.value > 0, "Must send ETH");
        
        contributions[msg.sender] += msg.value;
        totalRaised += msg.value;
        
        emit Contribution(msg.sender, msg.value);
        
        if (totalRaised >= goal) {
            emit GoalReached(totalRaised);
        }
    }
    
    // view:只读状态
    function getContribution(address contributor) public view returns (uint256) {
        return contributions[contributor];
    }
    
    // view:只读状态
    function getCampaignStatus() public view returns (
        uint256 _totalRaised,
        uint256 _remaining,
        uint256 _timeLeft,
        bool _goalReached
    ) {
        _totalRaised = totalRaised;
        _remaining = goal > totalRaised ? goal - totalRaised : 0;
        _timeLeft = deadline > block.timestamp ? deadline - block.timestamp : 0;
        _goalReached = totalRaised >= goal;
    }
    
    // pure:不读取状态
    function calculateShare(uint256 contribution, uint256 total) 
        public 
        pure 
        returns (uint256) 
    {
        require(total > 0, "Total is zero");
        return (contribution * 100) / total;
    }
    
    // 非view非pure:修改状态
    function claimFunds() public {
        require(msg.sender == owner, "Not owner");
        require(totalRaised >= goal, "Goal not reached");
        require(!claimed, "Already claimed");
        require(block.timestamp >= deadline, "Campaign not ended");
        
        claimed = true;
        payable(owner).transfer(totalRaised);
        
        emit FundsWithdrawn(owner, totalRaised);
    }
    
    // 非view非pure:修改状态
    function refund() public {
        require(block.timestamp >= deadline, "Campaign not ended");
        require(totalRaised < goal, "Goal was reached");
        require(contributions[msg.sender] > 0, "No contribution");
        
        uint256 amount = contributions[msg.sender];
        contributions[msg.sender] = 0;
        
        payable(msg.sender).transfer(amount);
    }
}

5.2 计算器合约 #

solidity
contract Calculator {
    // pure:纯计算
    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    function subtract(uint256 a, uint256 b) public pure returns (uint256) {
        require(a >= b, "Underflow");
        return a - b;
    }
    
    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a * b;
    }
    
    function divide(uint256 a, uint256 b) public pure returns (uint256) {
        require(b != 0, "Division by zero");
        return a / b;
    }
    
    function modulo(uint256 a, uint256 b) public pure returns (uint256) {
        require(b != 0, "Division by zero");
        return a % b;
    }
    
    function power(uint256 base, uint256 exponent) public pure returns (uint256) {
        return base ** exponent;
    }
    
    function sqrt(uint256 x) public pure returns (uint256) {
        if (x == 0) return 0;
        uint256 z = (x + 1) / 2;
        uint256 y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
        return y;
    }
}

六、最佳实践 #

6.1 选择正确的修饰符 #

solidity
contract BestPractices {
    uint256 public value;
    
    // ✓ 纯计算:使用pure
    function calculate(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    // ✓ 读取状态:使用view
    function getValue() public view returns (uint256) {
        return value;
    }
    
    // ✓ 接收ETH:使用payable
    function deposit() public payable {}
    
    // ✓ 修改状态:无修饰符
    function setValue(uint256 _value) public {
        value = _value;
    }
}

6.2 Gas优化 #

solidity
contract GasOptimization {
    uint256 public value;
    
    // 从外部调用view/pure函数不消耗Gas
    // 但从内部调用会消耗Gas
    
    function externalView() external view returns (uint256) {
        return value;
    }
    
    function callFromInternal() public view returns (uint256) {
        // 内部调用view函数消耗Gas
        return externalView();
    }
}

七、总结 #

修饰符要点:

修饰符 读取状态 修改状态 接收ETH Gas消耗(外部调用)
无修饰 消耗Gas
view 不消耗Gas
pure 不消耗Gas
payable 消耗Gas

使用建议:

  • 纯计算使用pure
  • 只读状态使用view
  • 需要接收ETH使用payable
  • 修改状态不使用修饰符

接下来,让我们学习特殊函数!

最后更新:2026-03-27