返回文章列表

文章

Solidity中internal 和 external的区别

internal 和 external 是用于控制函数和状态变量可见性的关键字,它们之间的主要区别在于 谁可以调用或访问它们

目录
  1. 1. internal 关键字
  2. 2. external 关键字
  3. 总结比较
  4. 📎 参考文章

在 Solidity 中,internalexternal 是用于控制函数和状态变量可见性的关键字,它们之间的主要区别在于 谁可以调用或访问它们

1. internal 关键字#

  • 可见性: internal 函数和状态变量只能在当前合约内部被访问,或者被继承自当前合约的派生合约访问。它们不能直接从外部(例如,通过交易或DApp)调用。
  • 调用方式:
    • 在当前合约内部: 可以直接通过函数名调用,例如 myInternalFunction();
    • 在派生合约中: 派生合约可以直接调用继承的 internal 函数。
  • 用途: 通常用于定义辅助函数或状态变量,这些辅助功能只在合约的内部逻辑中需要,而不需要对外暴露。这有助于封装和代码组织。 示例:
pragma solidity ^0.8.0;

contract ParentContract {
    uint internal internalNumber = 10;

    function _calculateSum(uint a, uint b) internal pure returns (uint) {
        return a + b;
    }

    function getInternalNumber() public view returns (uint) {
        return internalNumber;
    }

    function useInternalFunction() public view returns (uint) {
        return _calculateSum(5, 7); // 在内部调用 internal 函数
    }
}

contract ChildContract is ParentContract {
    function getSumFromParent(uint x, uint y) public view returns (uint) {
        return _calculateSum(x, y); // 子合约可以调用父合约的 internal 函数
    }

    function getParentInternalNumber() public view returns (uint) {
        return internalNumber; // 子合约可以访问父合约的 internal 状态变量
    }
}

2. external 关键字#

  • 可见性: external 函数只能从合约外部被调用。它们不能在合约内部直接通过函数名调用(但可以通过 this.functionName() 进行外部调用,这会产生一个实际的EVM消息调用,成本较高,通常不建议这样做)。
  • 调用方式:
    • 从外部账户或DApp: external 函数是合约对外接口的一部分,可以通过交易或DApp调用。
    • 从其他合约: 其他合约可以通过合约地址和ABI接口调用 external 函数。
  • 用途: 主要用于定义合约的公共接口,即那些需要被外部用户或DApp调用的函数。当函数只期望从外部调用,并且不涉及内部函数之间的频繁相互调用时,使用 externalpublic 更节省Gas,特别是对于数组等复杂数据类型的参数。 示例:
pragma solidity ^0.8.0;

contract MyContract {
    uint public counter;

    function incrementCounterExternal() external {
        counter++;
    }

    function getCounter() public view returns (uint) {
        return counter;
    }

    // 尝试在内部直接调用 external 函数会报错:
    // function internalCallExternal() internal {
    //     incrementCounterExternal(); // 编译错误!
    // }

    // 如果非要在内部调用 external 函数,需要使用 this.functionName()
    function internalCallExternalViaThis() public {
        this.incrementCounterExternal(); // 这会产生一个额外的EVM消息调用,消耗更多Gas
    }
}

总结比较#

**特性****internal****external**
**可访问性**当前合约内部和继承合约只能从合约外部
**内部调用**可以,直接通过函数名不可以,除非使用 `this.functionName()` (不推荐)
**外部调用**不可以可以
**Gas 效率**通常用于内部逻辑,不产生额外Gas开销对于大型数组参数,通常比 `public` 更节省Gas
**主要用途**合约内部辅助函数,状态变量的封装合约的公共接口,供外部调用

选择建议:

  • 如果你希望一个函数只能在合约内部被调用,或者被它的子合约调用,使用 internal
  • 如果你希望一个函数只能从合约外部被调用(例如,用户或DApp),并且不希望它被当前合约中的其他函数直接调用,那么使用 external
  • 如果你希望一个函数既能从外部调用,也能在合约内部直接调用,那么使用 publicpublicinternalexternal 的结合体。 在实际开发中,根据函数的实际需求选择合适的可见性修饰符,可以提高合约的安全性和Gas效率。

📎 参考文章#