solidity多重继承与C3线性化

本文探讨了在Solidity中如何处理多重继承带来的棱形继承问题,通过C3线性化算法确定方法调用顺序。算法过程包括合并线性化序列并递归计算,以确保MRO的正确性。举例说明了如何根据MRO计算特定类的线性化结果,并指出在Solidity中`is`关键字的特殊行为。

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值