深入理解Solidity之源文件及合約結構——Solidity中文文檔(4)

image

寫在前面:HiBlock區塊鏈社區成立了翻譯小組,翻譯區塊鏈相關的技術文檔及資料,本文爲Solidity文檔翻譯的第四部分《深入理解Solidity之源文件及合約結構》,特發佈出來邀請solidity愛好者、開發者做公開的審校,您可以添加微信baobaotalk_com,驗證輸入「solidity」,然後將您的意見和建議發送給我們,也可以在文末「留言」區留言,有效的建議我們會採納及合併進下一版本,同時將送一份小禮物給您以示感謝。

Solidity 源文件結構

源文件中可以包含任意多個合約定義、導入指令和雜注指令。

1

版本雜注

爲了避免未來被可能引入不兼容變更的編譯器所編譯,源文件可以(也應該)被所謂的版本 雜注pragma所註解。 我們力圖把這類變更做到儘可能小,特別是,我們需要以一種當修改語義時必須同步修改語法的方式引入變更,當然這有時候也難以做到。 因此,至少對含重大變更的版本,通讀變更日誌永遠是好辦法。 這些版本的版本號始終是 0.x.0 或者 x.0.0 的形式。

版本雜注使用如下:

pragma solidity ^0.4.0;

這樣,源文件將既不允許低於 0.4.0 版本的編譯器編譯, 也不允許高於(包含) 0.5.0 版本的編譯器編譯(第二個條件因使用 ^ 被添加)。 這種做法的考慮是,編譯器在 0.5.0 版本之前不會有重大變更,所以可確保源代碼始終按預期被編譯。 上面例子中不固定編譯器的具體版本號,因此編譯器的補丁版也可以使用。

可以使用更復雜的規則來指定編譯器的版本,表達式遵循 npm 版本語義。

image

2

導入其他源文件

語法與語義

雖然 Solidity 不知道 「default export」 爲何物, 但是 Solidity 所支持的導入語句,其語法同 JavaScript(從 ES6 起)非常類似。

image

在全局層面上,可使用如下格式的導入語句:

import 「filename」;

此語句將從 「filename」 中導入所有的全局符號到當前全局作用域中(不同於 ES6,Solidity 是向後兼容的)。

import * as symbolName from 「filename」;

…創建一個新的全局符號 symbolName,其成員均來自 「filename」 中全局符號。

import {symbol1 as alias, symbol2} from 「filename」;

…創建新的全局符號 alias 和 symbol2,分別從 「filename」 引用 symbol1 和 symbol2 。

另一種語法不屬於 ES6,但或許更簡便:

import 「filename」 as symbolName;

這條語句等同於

import * as symbolName from 「filename」;

路徑

上文中的 filename 總是會按路徑來處理,以 / 作爲目錄分割符、以 . 標示當前目錄、以 .. 表示父目錄。 當 . 或 .. 後面跟隨的字符是 / 時,它們才能被當做當前目錄或父目錄。 只有路徑以當前目錄 . 或父目錄 .. 開頭時,才能被視爲相對路徑。

用 import 「./x」 as x; 語句導入當前源文件同目錄下的文件 x 。 如果用 import 「x」 as x; 代替,可能會引入不同的文件(在全局 include directory 中)。

最終導入哪個文件取決於編譯器(見下文)到底是怎樣解析路徑的。 通常,目錄層次不必嚴格映射到本地文件系統, 它也可以映射到能通過諸如 ipfs,http 或者 git 發現的資源。

在實際的編譯器中使用

當運行編譯器時,它不僅能指定如何發現路徑的第一個元素,還可指定路徑前綴 重映射remapping。 例如,github.com/ethereum/dapp-bin/library 會被重映射到 /usr/local/dapp-bin/library , 此時編譯器將從重映射位置讀取文件。如果重映射到多個路徑,優先嚐試重映射路徑最長的一個。 這允許將比如 「」 被映射到 「/usr/local/include/solidity」 來進行「回退重映射」。 同時,這些重映射可取決於上下文,允許你配置要導入的包,比如同一個庫的不同版本。

solc:

對於 solc(命令行編譯器),這些重映射以 context:prefix=target 形式的參數提供。 其中,context: 和 =target 部分是可選的(此時 target 默認爲 prefix )。 所有重映射的值都是被編譯過的常規文件(包括他們的依賴),這個機制完全是向後兼容的(只要文件名不包含 = 或 : ), 因此這不是一個破壞性修改。 在 content 目錄或其子目錄中的源碼文件中,所有導入語句裏以 prefix 開頭的導入文件都將被用 target 替換 prefix 來重定向。

舉個例子,如果你已克隆 github.com/ethereum/dapp-bin/ 到本地 /usr/local/dapp-bin , 可在源文件中使用:

import 「github.com/ethereum/dapp-bin/library/iterable_mapping.sol」 as it_mapping;

然後運行編譯器:

solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

舉個更復雜的例子,假設你依賴了一些使用了非常舊版本的 dapp-bin 的模塊。 舊版本的 dapp-bin 已經被 checkout 到 /usr/local/dapp-bin_old ,此時你可使用:

solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \

module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ source.sol

這樣, module2 中的所有導入都指向舊版本,而 module1 中的導入則獲取新版本。

注意, solc 只允許包含來自特定目錄的文件:它們必須位於顯式地指定的源文件目錄(或子目錄)中,或者重映射的目標目錄(或子目錄)中。 如果你想直接用絕對路徑來包含文件,只需添加重映射 =/。

如果有多個重映射指向一個有效文件,那麼具有最長公共前綴的重映射會被選用。

Remix:

Remix 提供一個爲 github 源代碼平臺的自動重映射,它將通過網絡自動獲取文件: 比如,你可以使用

import 「github.com/ethereum/dapp-bin/library/iterable_mapping.sol」 as it_mapping;

導入一個 map 迭代器。

未來, Remix 可能支持其他源代碼平臺。

3

注 釋

可以使用單行註釋(//)和多行註釋(//)

// 這是一個單行註釋。

/*

這是一個多行註釋。

*/

此外,有另一種註釋稱爲 natspec 註釋,其文檔還尚未編寫。 它們是用三個反斜槓(///)或雙星號開頭的塊(/* … /)書寫,它們應該直接在函數聲明或語句上使用。 可在註釋中使用 Doxygen 樣式的標籤來文檔化函數、 標註形式校驗通過的條件,和提供一個當用戶試圖調用一個函數時顯示給用戶的 確認文本

在下面的例子中,我們記錄了合約的標題、兩個入參和兩個返回值的說明:

pragma solidity ^0.4.0;

/* @title 形狀計算器。 /

**contract **shapeCalculator {
* /* @dev** 求矩形表明面積與周長。

    • @param w 矩形寬度。 *
    • @param h 矩形高度。 *
    • @return s 求得表面積。 *
    • @return p 求得周長。 *
  • /
    function rectangle(uint w, uint h) returns (uint s, uint p) {
    s = w * h;
    p** =* 2 (w + h);
    }

}

合約結構

在 Solidity 中,合約類似於面向對象編程語言中的類。 每個合約中可以包含 狀態變量、 函數、 函數修飾器、事件、 結構類型、 和 枚舉類型 的聲明,且合約可以從其他合約繼承。

## 1 狀態變量

狀態變量是永久地存儲在合約存儲中的值。

pragma solidity ^0.4.0;

contract SimpleStorage {
* uint* storedData; // 狀態變量
// …

}

有效的狀態變量類型參閱 類型 章節, 對狀態變量可見性有可能的選擇參閱 Visibility and Getters 。

2 函 數

函數是合約中代碼的可執行單元。

pragma solidity ^0.4.0;

contract SimpleAuction {
function bid() public payable { // 函數
// …
}

}

Function Calls 可發生在合約內部或外部,且函數對其他合約有不同程度的可見性( Visibility and Getters)。

3 函數修飾器

函數修飾器可以用來以聲明的方式改良函數語義(參閱合約章節中 Function Modifiers)。

pragma solidity ^0.4.11;

**contract **Purchase {
address public seller;

*modifier **onlySeller() { // 修飾器*
require(msg.sender == seller);
_;
}

* function* abort() *public **onlySeller { // Modifier usage*
// …
}

}

4 事 件

事件是與以太坊虛擬機日誌工具的方便接口。

pragma solidity ^0.4.0;

**contract **SimpleAuction {

event HighestBidIncreased(address bidder, uint amount);* // 事件*

* function* bid() public payable {
// …
HighestBidIncreased(msg.sender, msg.value); // 觸發事件
}

}

有關如何聲明事件和如何在 dapp 中使用事件的信息,參閱合約章節中的 Events。

5 結構類型

結構是可以將幾個變量分組的自定義類型(參閱類型章節中的 Structs)。

pragma solidity ^0.4.0;

**contract **Ballot {
* struct **Voter { // 結構*
uint weight;
bool voted;
address delegate;
uint vote;
}

}

6 枚舉類型

枚舉可用來創建有一定數量的值的自定義類型(參閱類型章節中的 Enums)。

pragma solidity ^0.4.0;

contract Purchase {
enum State { Created, Locked, Inactive } // 枚舉

}

本文內容來源於HiBlock區塊鏈社區翻譯小組,感謝全體譯者的辛苦工作。

以下是我們的社區介紹,歡迎各種合作、交流、學習:)

image