阅读(474) (13)

可见性和getter

2022-05-13 09:59:22 更新

状态变量可见性

public

公共状态变量与内部变量的不同之处仅在于编译器会自动为它们生成 getter 函数,这允许其他合约读取它们的值。当在同一个合约中使用时,外部访问(例如this.x)调用 getter,而内部访问(例如x)直接从存储中获取变量值。不生成设置器函数,因此其他合约无法直接修改它们的值。

internal

内部状态变量只能从它们在衍生合同中定义的合同中访问。它们不能被外部访问。这是状态变量的默认可见性级别。

private

私有状态变量类似于内部变量,但它们在派生合约中不可见。

警告

制作某些东西privateinternal仅阻止其他合约读取或修改信息,但它仍然对区块链之外的整个世界可见。

功能可见性

Solidity 知道两种函数调用:确实创建实际 EVM 消息调用的外部函数调用和不创建实际 EVM 消息调用的内部函数调用。此外,派生合约可能无法访问内部功能。这产生了四种类型的功能可见性。

external

外部函数是合约接口的一部分,这意味着它们可以从其他合约和交易中调用。f不能在内部调用外部函数(即f()不工作,但this.f()工作)。

public

公共函数是合约接口的一部分,可以在内部调用,也可以通过消息调用。

internal

内部函数只能从当前合约或从它派生的合约中访问。它们不能被外部访问。由于它们没有通过合约的 ABI 暴露给外部,它们可以采用内部类型的参数,如映射或存储引用。

private

私有函数类似于内部函数,但它们在派生合约中不可见。

警告

制作某些东西privateinternal仅阻止其他合约读取或修改信息,但它仍然对区块链之外的整个世界可见。

可见性说明符在状态变量的类型之后以及函数的参数列表和返回参数列表之间给出。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    function f(uint a) private pure returns (uint b) { return a + 1; }
    function setData(uint a) internal { data = a; }
    uint public data;
}

在以下示例中D, 可以调用c.getData()以检索 data状态存储中的值,但不能调用f。ContractE派生自 C,因此可以调用compute.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    uint private data;

    function f(uint a) private pure returns(uint b) { return a + 1; }
    function setData(uint a) public { data = a; }
    function getData() public view returns(uint) { return data; }
    function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}

// This will not compile
contract D {
    function readData() public {
        C c = new C();
        uint local = c.f(7); // error: member `f` is not visible
        c.setData(3);
        local = c.getData();
        local = c.compute(3, 5); // error: member `compute` is not visible
    }
}

contract E is C {
    function g() public {
        C c = new C();
        uint val = compute(3, 5); // access to internal member (from derived to parent contract)
    }
}

吸气剂函数

编译器会自动为所有公共状态变量创建 getter 函数。对于下面给出的合约,编译器将生成一个调用的函数,该函数data不接受任何参数并返回一个uint状态变量的值data。状态变量可以在声明时进行初始化。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    uint public data = 42;
}

contract Caller {
    C c = new C();
    function f() public view returns (uint) {
        return c.data();
    }
}

getter 函数具有外部可见性。如果符号在内部被访问(即没有this.),它的计算结果是一个状态变量。如果它被外部访问(即使用this.),它的计算结果是一个函数。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract C {
    uint public data;
    function x() public returns (uint) {
        data = 3; // internal access
        return this.data(); // external access
    }
}

如果您有一个public数组类型的状态变量,那么您只能通过生成的 getter 函数检索数组的单个元素。这种机制的存在是为了避免在返回整个阵列时产生高气体成本。您可以使用参数来指定要返回的单个元素,例如 myArray(0). 如果要在一次调用中返回整个数组,则需要编写一个函数,例如:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract arrayExample {
    // public state variable
    uint[] public myArray;

    // Getter function generated by the compiler
    /*
    function myArray(uint i) public view returns (uint) {
        return myArray[i];
    }
    */

    // function that returns entire array
    function getArray() public view returns (uint[] memory) {
        return myArray;
    }
}

现在您可以使用getArray()来检索整个数组,而不是 myArray(i),它每次调用都返回一个元素。

下一个例子更复杂:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract Complex {
    struct Data {
        uint a;
        bytes3 b;
        mapping (uint => uint) map;
        uint[3] c;
        uint[] d;
        bytes e;
    }
    mapping (uint => mapping(bool => Data[])) public data;
}

它生成以下形式的函数。结构中的映射和数组(字节数组除外)被省略了,因为没有很好的方法来选择单个结构成员或为映射提供键:

function data(uint arg1, bool arg2, uint arg3)
    public
    returns (uint a, bytes3 b, bytes memory e)
{
    a = data[arg1][arg2][arg3].a;
    b = data[arg1][arg2][arg3].b;
    e = data[arg1][arg2][arg3].e;
}