Solidity语言详解——函数和状态变量的可见性及Getter函数

本文内容包括 Solidity 语言的状态变量可见性、函数可见性和 Getter 函数。由于 Solidity 有两种函数调用(内部调用不会产生实际的 EVM 调用或称为“消息调用”,而外部调用则会产生一个 EVM 调用), 函数和状态变量有四种可见性类型。 函数可以指定为 externalpublicinternal 或者 private。 对于状态变量,不能设置为 external ,默认是 internal

状态变量可见性

public

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

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。合约 E派生自合约 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函数

编译器会自动为所有公共状态变量创建 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;
}

猜你喜欢

转载自blog.csdn.net/zyq55917/article/details/124320963