Estoy leyendo la sección sobre constructores heredados en la documentación aquí .
El ejemplo dado me confunde más que entender el concepto, y no explica la parte más importante, el orden de ejecución.
Para resumir la documentación, dice en el siguiente ejemplo:
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
hereda el Base(7)
constructor y Derived2
usa una sintaxis similar a un modificador de Base(_y * _y)
.
Pero lo que NO explica es cómo se ejecutan realmente. Tomemos un ejemplo
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;
}
}
¿ x = _y
Se ejecuta en cada herencia ANTES del constructor base? ¿o después?
Normalmente, en cualquier programación orientada a objetos, usa notaciones como super()
para indicar explícitamente cómo se ejecutará el constructor principal.
En caso de objetivo-c
- (void) init {
[super init];
// do something
}
y
- (void) init {
// do something
[super init];
}
hacer una gran diferencia ya que el orden de ejecución es diferente. Y la //do something
parte puede incluso utilizar el resultado de [super init]
.
Entonces, ¿cómo funciona esto en Solidity? Si puede, comparta también la fuente de la explicación. No puedo encontrar esto en la documentación, así que no sé dónde más puedo encontrar esto.
No pude encontrar un documento oficial para explicar esto. Pero cuando nos referimos al código, el flujo del programa podemos asumir que el constructor del Base
es llamado antes que el Derived
constructor.
En el primero , Derived1 is Base(7)
la Base(7)
sintaxis tiene el aspecto del constructor con el valor 7
que se pasa y probablemente esté llamando antes de ir al Derived1
constructor cuando se considera el orden declarado.
En el segundo ya que los documentos dicen,
la forma en que se invocaría un modificador
y como siempre los modificadores se ejecutan antes de entrar en el cuerpo de la función, podemos llegar a la conclusión de que Base(7)
se ejecuta primero.
Con las suposiciones anteriores, probé el siguiente código en Remix con _y=10
la creación del contrato. La getX
función en ambos contratos devolvió un valor 10
pero no 7
en Derived1
o 100
que está (_y*_y)
en Derived2
. Eso significa que el Base
constructor se ejecuta primero y el valor de x
se sobrescribe 10
en x = _y;
el cuerpo de los Derived
constructores. Creo que esto prueba que Base
se espera primero al constructor.
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;
}
}
(si no tiene x = _y
en Derived
los constructores, los valores devueltos son 7
y 100
)
Los documentos actuales de Solidity brindan la respuesta en este párrafo: https://docs.soliditylang.org/en/v0.8.13/contracts.html#multiple-inheritance-and-linearization
Básicamente: Solidity garantiza que todos los constructores de las clases base serán llamados antes que un constructor de clases derivadas. Y el constructor de clases derivadas no puede cambiar el orden en que Solidity llama a los constructores de clases base.
Pero esto es un poco complicado ya que Solidity tiene que lidiar con la herencia múltiple. Como se explica en los documentos, el gráfico de herencia para la clase derivada se linealiza mediante la linealización C3 , que produce una lista ordenada de clases donde cada clase derivada aparece después de todas sus clases base. Los constructores se llaman en el orden de la lista. Cuando tiene más de una clase base, el orden en que aparecen en la lista se verá afectado por el orden en que aparecen en la lista de herencia (es decir, después de is
).
Citando de la documentación:
Un área donde la linealización de herencia es especialmente importante y quizás no tan clara es cuando hay varios constructores en la jerarquía de herencia. Los constructores siempre se ejecutarán en el orden linealizado, independientemente del orden en que se proporcionen sus argumentos en el constructor del contrato heredado. Por ejemplo:
// 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
yDerived2
, ¿quiso decir que los nombres de las funciones fueranDerived1
yDerived2
, respectivamente, para que fueran constructores, en lugar deDerived
en ambos?Vlad