Ich lese den Abschnitt über geerbte Konstruktoren in der Dokumentation hier .
Das gegebene Beispiel verwirrt mich, als das Konzept zu verstehen, und es erklärt nicht den wichtigsten Teil, die Ausführungsreihenfolge.
Um die Dokumentation zusammenzufassen, heißt es im folgenden Beispiel:
pragma solidity ^0.4.0;
contract Base {
uint x;
function Base(uint _x) { x = _x; }
}
contract Derived1 is Base(7) {
function Derived1(uint _y) {
}
}
contract Derived2 is Base{
function Derived2(uint _y) Base(_y * _y) {
}
}
Derived1
erbt Base(7)
den Konstruktor und Derived2
verwendet eine Modifikator-ähnliche Syntax von Base(_y * _y)
.
Aber was es NICHT erklärt, ist, wie sie tatsächlich ausgeführt werden. Nehmen wir ein Beispiel
contract Base {
public uint x;
function Base(uint _x) { x = _x; }
}
contract Derived1 is Base(7) {
function Derived1(uint _y) {
x = _y;
}
}
contract Derived2 is Base {
function Derived2(uint _y) Base(_y * _y) {
x = _y;
}
}
Wird das x = _y
in jeder Vererbung VOR dem Basiskonstruktor ausgeführt? oder danach?
Normalerweise verwenden Sie in jeder objektorientierten orientierten Programmierung Notationen super()
, die explizit angeben, wie der übergeordnete Konstruktor ausgeführt wird.
Im Falle von Ziel-c
- (void) init {
[super init];
// do something
}
und
- (void) init {
// do something
[super init];
}
machen einen großen Unterschied, da die Ausführungsreihenfolge unterschiedlich ist. Und das //do something
Teil kann sogar das Ergebnis von verwenden [super init]
.
Wie funktioniert dieses Ding in Solidity? Wenn möglich, teilen Sie bitte auch die Quelle für die Erklärung mit. Ich kann das in der Dokumentation nicht finden, also weiß ich nicht, wo ich es sonst finden kann.
Ich konnte kein offizielles Dokument finden, um dies zu erklären. Aber wenn wir uns auf den Code, den Ablauf des Programms beziehen, können wir davon ausgehen, dass der Konstruktor von Base
vor dem Derived
Konstruktor aufgerufen wird.
In der ersten sieht Derived1 is Base(7)
die Base(7)
Syntax wie der Konstruktor aus, wobei der Wert 7
übergeben wird und wahrscheinlich aufgerufen wird, bevor der Derived1
Konstruktor aufgerufen wird, wenn die deklarierte Reihenfolge berücksichtigt wird.
In der zweiten, da die Dokumente sagen,
die Art und Weise, wie ein Modifikator aufgerufen würde
und da immer die Modifikatoren ausgeführt werden, bevor wir in den Funktionskörper gehen, können wir zu dem Schluss kommen, dass Base(7)
zuerst ausgeführt wird.
Mit den obigen Annahmen habe ich den folgenden Code in Remix mit _y=10
bei der Vertragserstellung ausprobiert. Die getX
Funktion in beiden Verträgen hat einen Wert von zurückgegeben 10
, aber nicht 7
in Derived1
oder 100
was (_y*_y)
in ist Derived2
. Das bedeutet, dass Base
der Konstruktor zuerst ausgeführt wird und der Wert von im Körper der Konstruktoren durch x
überschrieben wird. Ich denke, dies beweist, dass der Konstruktor zuerst erwartet wird.10
x = _y;
Derived
Base
pragma solidity ^0.4.0;
contract Base {
uint x;
function Base(uint _x) { x = _x; }
}
contract Derived1 is Base(7) {
function Derived1(uint _y) {
x = _y;
}
function getX() constant returns(uint){
return x;
}
}
contract Derived2 is Base {
function Derived2(uint _y) Base(_y * _y) {
x = _y;
}
function getX() constant returns(uint){
return x;
}
}
x = _y
(Wenn Sie in Konstruktoren keine Derived
zurückgegebenen Werte haben, sind 7
und 100
)
Aktuelle Solidity-Dokumente liefern die Antwort in diesem Absatz: https://docs.soliditylang.org/en/v0.8.13/contracts.html#multiple-inheritance-and-linearization
Grundsätzlich gilt: Solidity garantiert, dass alle Konstruktoren der Basisklassen vor einem Konstruktor einer abgeleiteten Klasse aufgerufen werden. Und der Konstruktor der abgeleiteten Klasse kann die Reihenfolge, in der Konstruktoren der Basisklassen von Solidity aufgerufen werden, nicht ändern.
Dies ist jedoch etwas kompliziert, da Solidity mit Mehrfachvererbung umgehen muss. Wie in der Dokumentation erläutert, wird der Vererbungsgraph für die abgeleitete Klasse mit C3-Linearisierung linearisiert , was eine geordnete Liste von Klassen ergibt, in der jede abgeleitete Klasse nach all ihren Basisklassen erscheint. Die Konstruktoren werden in der Reihenfolge der Liste aufgerufen. Wenn Sie mehr als eine Basisklasse haben, wird die Reihenfolge, in der sie in der Liste erscheinen, von der Reihenfolge beeinflusst, in der sie in der Vererbungsliste aufgeführt sind (dh nach dem is
).
Zitat aus der Dokumentation:
Ein Bereich, in dem die Vererbungslinearisierung besonders wichtig und vielleicht nicht so klar ist, ist, wenn es mehrere Konstruktoren in der Vererbungshierarchie gibt. Die Konstruktoren werden immer in der linearisierten Reihenfolge ausgeführt, unabhängig von der Reihenfolge, in der ihre Argumente im Konstruktor des erbenden Vertrags bereitgestellt werden. Zum Beispiel:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Base1 { constructor() {} } contract Base2 { constructor() {} } // Constructors are executed in the following order: // 1 - Base1 // 2 - Base2 // 3 - Derived1 contract Derived1 is Base1, Base2 { constructor() Base1() Base2() {} } // Constructors are executed in the following order: // 1 - Base2 // 2 - Base1 // 3 - Derived2 contract Derived2 is Base2, Base1 { constructor() Base2() Base1() {} } // Constructors are still executed in the following order: // 1 - Base2 // 2 - Base1 // 3 - Derived3 contract Derived3 is Base2, Base1 { constructor() Base1() Base2() {} }
Vlad
Ajoy Bhatia
Derived1
undDerived2
, die Funktionsnamen alsDerived1
bzw.Derived2
, sodass sie Konstruktoren sind, anstattDerived
in beiden?Vlad