solidity多重繼承與C3線性化

solidity支持多重繼承,就不可避免地要面對棱形繼承這種問題。solidity的解決方案與Python類似,使用C3 線性化來確定方法的調用順序,也叫方法解析順序(Method Resolution Order,MRO)
該算法過程如下:

如果繼承至一個基類: class B(A) 這時B的mro序列爲[B,A] 如果繼承至多個基類 class B(A1,A2,A3 …) 這時B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) …, [A1,A2,A3]) merge操作就是C3算法的核心。 遍歷執行merge操作的序列,如果一個序列的第一個元素,是其他序列中的第一個元素,或不在其他序列出現,則從所有執行merge操作序列中刪除這個元素,合併到當前的mro中。 merge操作後的序列,繼續執行merge操作,直到merge操作的序列爲空。 如果merge操作的序列無法爲空,則說明不合法。

本文約定以下符號:

  • L(A) : 類A的線性化結果的簡寫
  • [B, C, D] : 某個類的線性化結果的具體值
  • merge(…) merge 算法
  • A->B,C:類A繼承自B和C

利用以上符號,該算法歸納如下:

  • 對於 A->B,C,D,則有 L(A) = [A] + merge(L(B), L©, L(D) , [B,C,D])
  • 對於merge([X], [Y], [Z]),便利X序列的第一個元素,若該元素是其他序列中的第一個元素,或不在其他序列出現,則從所有序列中刪除這個元素,合併到MRO中,繼續遍歷X的下一個元素;否則保留該元素,並依然繼續遍歷下一個元素。
  • 直到merge操作的序列爲空

我們以以下繼承關係爲例:

O
A -> O
B -> O
C -> O
K1 -> B, A
K2 -> C, A
Z -> K2, K1

求:L(Z)
根據MRO算法規則,
L(Z) = [Z] + merge(L(K2), L(K1), [K1, K2])
要想求L(Z),我們得先求出 L(K2) 與 L(K1),一直遞歸到L(O);

L(O) = [O]
L(A) = [A] + merge(L(O), [O])
	 = [A] + merge([O], [O])//遍歷merge中的第一個序列的第一個元素O,該元素在第二個序列中位於首位,故將O合併到MRO,merge被清空。
	 = [A, O]
L(B) = [B, O]
L(C) = [C, O]
L(K1)= [K1] + merge(L(B), L(A), [B,A])
	 = [K1] + merge([B,O], [A,O], [B,A])//先處理B,符合條件,合併入MRO
	 = [K1, B] + merge([O], [A,O], [A])//再處理O,不符合條件,保留O,進而處理A,符合條件
	 = [K1, B, A] + merge([O], [O])
	 = [K1, B, A, O]
L(K2)= [K2, B, A, O]

有了計算L(K1)的過程,L(Z)也就迎刃而解了,有興趣的同學可自行演算,答案是:
[Z, K2, C, K1, B, A, O]

回到上一篇博文的例子中:

contract Ancestor{
    function Func(uint256 val)public pure returns(uint256){
        return val;
    }
}
contract BaseA is Ancestor{
    function Func(uint256 val)public pure returns(uint256){
        return Ancestor.Func(val * 2);
    }
}
contract BaseB is Ancestor{
    function Func(uint256 val)public pure returns(uint256){
        return Ancestor.Func(val * 4);
    }
}
contract Final is BaseA, BaseB {
}

調用Final.Func(N) 的結果是 4N,這裏需要小心一點:solidity的is的實現與普通的有區別:Final is BaseA, BaseB 等效於 Final-> BaseB, BaseA

L(Final) = [Final] + merge([BaseB, Ancestor], [BaseA, Ancestor], [BAseB, BaseA])
		 = [Final, BaseB, BaseA, Ancestor]

所以調用Final.Func(N) 實際上是調用BaseB.Func(N)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章