Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Una Dudilla Sobre C#

Iniciado por CoLSoN2, 10 de Noviembre de 2004, 07:40:30 PM

« anterior - próximo »

CoLSoN2

 Últimamente me estoy mirando mucho C# y la verdad es que todo lo que he leído me encanta, pero hay algo que no acabo de captar. Todo lo que he leído lleva a pensar que todo el diseño lo han hecho pensando en que haya la menor parte de errores difíciles de resolver por el programador, pero luego me encuentro con esto:

- Tengo una clase A con un metodo virtual foo(), y otra clase B que deriva de A i redefine ese método:

- En el constructor/destructor de la clase base, si llamo a foo() se llamará al de B(). ¿eso como se come? En C++  se llamaría al de A y me parece lo más obvio porque, en el constructor, la parte de B no estaría todavía inicializada (ya que C# inicializa los campos a 0 y demás), y en el destructor la parte de B ya se habría destruido.

¿alguien sabe decirme porqué lo han hecho así?
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

_Grey

 En C++ seria suficiente con A::foo(); para llamar al foo() de la clase base A, o eso creo.... :unsure:

PD:seguro que si lo llamas desde el constructor/destructor de B, se llama al de A??? diria que no...(en C++)

BeRSeRKeR

 Hola.

Si te refieres a esto:

namespace Test
{
   public class A
   {
       public A()
       {
           foo();
       }

       protected virtual void foo()
       {
           System.Console.WriteLine("A::foo()");
       }
   }

   public class B : A
   {
       public B() : base()
       {
       }

       protected override void foo()
       {
           System.Console.WriteLine("B::foo()");
       }
   }

   public class Program
   {
       private static void Main(string[] args)
       {
           A a = new A();
           B b = new B();
       }
   }
}


el objeto del tipo A llama al método foo() de A y el B al método foo() de B.

Saludos.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

CoLSoN2

 
CitarPD:seguro que si lo llamas desde el constructor/destructor de B, se llama al de A??? diria que no...(en C++)
creo que no me has entendido o no me he explicado.. es justo lo contrario: si lo llamo desde A se llama al de B (desde un método normal es lo deseable, pero no desde el constructor/destructor).

@BeRSeRKeR: me refería a cuando se utiliza poliformismo; usando tus mismas clases A y B:




   public class Program
  {
      private static void Main(string[] args)
      {
          A a = new B();
      }
  }


Aquí se llamaría a B::foo() cuando todavía no se ha construído la parte del objeto correspondiente a B, por lo que si yo hago uso en ese método de campos o métodos de B, lo que ocurriría digamos que es "undefined" :P
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

CoLSoN2

 ¿Entonces nadie sabe nada sobre porqué lo han hecho así?
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

Buffon

 En C++ no funciona como tu dices tio :S

las funciones virtuales sirven para cuando una clase hereda de ti, y se construya a partir de la clase base, pueda ser invocada la funcion de la clase heredada y no de la clase base.

en el ejemplo anterior, si foo es virtual y haces esto:


CitarA *objeto = new B();

objeto ->foo();

se llamará a la funcion foo de B

en cambio si no defines la funcion foo como virtual y haces lo mismo

CitarA *objeto = new B();

objeto ->foo();

se llamará a la funcion foo de la clase A y no de la clase B

me he sabido explicar?

Buffon

Cita de: "CoLSoN2"¿Entonces nadie sabe nada sobre porqué lo han hecho así?
para mi entender era la forma más lógica de hacerlo :S

virtual -> preferencia hijos

no virtual -> funcion propia de la clase del primer tipo

esto ahora lo abré dejado más lioso :P

ejemplo

tenemos la clase cPrimitiva que tiene una función dibujar que escribe por pantalla esto: "cPrimitiva::dibujar" y tenemos 2 clases derivadas de cPrimitiva llamadas que implementan esa funcion dibujando en una ventana un triangulo y un cuadrado respectivamente, y no escriben nada.


que pasa si esta funcion es:

virtual

Citar
el siguiente codigo:

cPrimitiva *objeto = new cTriangulo();
objeto -> dibujar();
//dibujamos por pantalla un triangulo

objeto = new cRectangulo();
objeto -> dibujar();
//dibujamos un cuadrado por pantalla

objeto = new cPrimitiva();
objeto -> dibujar();
//escribimos por linea de comandos "cPrimitiva::dibujar"

no virtual

Citar
el siguiente codigo:

cPrimitiva *objeto = new cTriangulo();
objeto -> dibujar();
//escribimos por linea de comandos "cPrimitiva::dibujar"

objeto = new cRectangulo();
objeto -> dibujar();
//escribimos por linea de comandos "cPrimitiva::dibujar"

objeto = new cPrimitiva();
objeto -> dibujar();
//escribimos por linea de comandos "cPrimitiva::dibujar"

/** Hasta aquí el código de antes con las supuestas diferencias */

cTriangulo triangulo();
triangulo -> dibujar();
//dibuja por pantalla un triangulo


espero que haya quedado claro ahora por si antes me expliqué mal :S

esta es la gracia del POLIMORFISMO :) poder usar el Objeto Base como si fuera el Objeto heredado para poder compartir funciones :) y tener clases comunes para diversos tipos de datos "medianamente diferentes".

;)

CoLSoN2

 @Buffon: lo que dices es totalmente válido, pero te olvidas del detalle de que yo hablo de llamadas a esos métodos en los constructores. Prueba a ejecutar este código en C++ (si tiene algún fallo sintáctico corrígelo):

#include <stdio.h>
class A
{
public:
A()
{
foo();
}
virtual void foo()
{
printf("A");
}
};

class B : public A
{
public:
B() : A()
{
foo();
}
virtual void foo()
{
printf("B");
}
};

void main()
{
A* a = new B();
delete a;
};


Verás que muestra por pantalla: "AB", mientras que si lo portas a C#, verás que muestra "BB", es decir desde A::A() también llama a B::foo(), lo cual es estúpido porque la parte correspondiente a B del objeto aún no ha sido creada (también puedes probar la versión análoga con el destructor, y pasa lo mismo). Como supongo que sabes, en C# los campos de las clases se inicializan antes de llamar al constructor (numericos a 0 y demás), pero en este caso, al llamar a B::foo() desde A::A() (lo que ocurre en C#), como es aparte del objeto aún no se ha construido, no estarían inicializados los campos, y hacer uso de ellos (pensando que tendrían un valor determinado) podría dar lugar a errores difíciles de detectar.

He aquí un extracto del ebook "El Lenguaje de Programación C#", de José Antonio González Seco, respecto al tema:

Citar
Llamadas polimórficas en constructores

Es conveniente evitar en la medida de lo posible la realización de llamadas a métodos virtuales dentro de los constructores, ya que ello puede provocar errores muy difíciles de detectar debido a que se ejecuten métodos cuando la parte del objeto que manipulan aún no se ha sido inicializado. Un ejemplo de esto es el siguiente:

using System;

public class Base
{
   public Base()
   {
  Console.WriteLine("Constructor de Base");
  this.F();
   }

   public virtual void F()
   {
  Console.WriteLine("Base.F"); 
   }
}

public class Derivada:Base
{
   Derivada()
   {
  Console.WriteLine("Constructor de Derivada");
   }

   public override void F()
   {
  Console.WriteLine("Derivada.F()");
   }
   
   public static void Main()   
   {
  Base b = new Derivada(); 
   }

}

La salida por pantalla mostrada por este programa al ejecutarse es la siguiente:

Constructor de Base
Derivada.F()
Constructor de Derivada

Lo que ha ocurrido es lo siguiente: Al crearse el objeto Derivada se ha llamado a su constructor sin parámetros, que como no tiene inicializador implícitamente llama al constructor sin parámetros de su clase base. El constructor de Base realiza una llamada al método virtual F(), y como el verdadero tipo del objeto que se está construyendo es Derivada, entonces la versión del método virtual ejecutada es la redefinición del mismo incluida en dicha clase. Por último, se termina llamando al constructor de Derivada y finaliza la construcción del objeto.

Nótese que se ha ejecutado el método F() de Derivada antes que el código del constructor de dicha clase, por lo que si ese método manipulase campos definidos en Derivada que se inicializasen a través de constructor, se habría accedido a ellos antes de inicializarlos y ello seguramente provocaría errores de causas difíciles de averiguar.
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

Ffelagund

 Si está muy claro lo que es el polimorfismo, lo raro es que el destructor de la clase base llame a un método de una clase derivada, cuando se supone que si se está destruyendo la clase base (al ser el destructor virtual) ya se han destruido los objetos de la clase derivada. Este comportamiento (en teoria) puede provocar que el dtor de la clase base intente usar de manera indirecta miembros de clase derivada, con lo que algo petará ahí seguramente.
A ese respecto, C# funciona de manera diferente a C++, la cuestión es ¿por qué?
a href='http://www.typhoonlabs.com' target='_blank'>http://www.typhoonlabs.com

"no se... con un vuelo... indiferente"

synchrnzr

 De acuerdo con Ffelagund. Ahí tiene que fallar algo. Da ganas de abrir el C# y probar :ph34r:

sync

Vicente

 Hola,

usa new en vez de override y ya está solucionado (te referias a esto no?)


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ConsoleApplication1
{
   public class A
   {
       public A()
       {
           Console.WriteLine("1");
           foo();
       }

       public virtual void foo()
       {
           Console.WriteLine("A");
       }
   }

   public class B : A
   {
       public B() : base()
       {
           Console.WriteLine("2");
           foo();
       }

       public new void foo()
       {
           Console.WriteLine("B");
       }
   }

   class Program
   {
       static void Main(string[] args)
       {
           A a = new B();
           Console.ReadLine();
       }
   }
}


Un saludo,

Vicente

P.D.: con override pasa lo que tu dices, sale BB. He usado el visual 2005 con .NET 2.0, pero supongo que en el 1.1 pasa lo mismo.

Buffon

 no se yo lo veo bastante claro  :blink:


fijaos en los pasos que sigue durante el run-time:


Base b = new Derivada();


esto lo primero que hace es llamar al Constructor de la clase Derivada, que por defecto siempre llama, antes de todo al constructor de sus padres, veis un ejemplo en java, que cualquier constructor hace super(); intrinsicamente antes de nada.

con lo cual descomponemos en pasos:

Base b = new Derivada();

llama al constructor que el compilador traduce por:

public Derivada()
{
super();
bla bla bla
}

entonces que ocurre? primero se llama al constructor del padre, este al no ser "hijo de nadie" ejecuta la primera orden, que es mostrar por pantalla el mensaje:

Constructor de Base

y justo después llama a la función foo, que como es virtual, se ejecuta la que tenga asociado el "this." que en este caso, y siendo virtual al ejecutarse la funcion, y sólo repito por ser virtual se mira si el puntero "this." es de la clase actual o de algun hijo.

al ser de un hijo se llama a la funcion foo del hijo "Derivada" que muestra por pantalla el mensaje :)

Derivada.F()

por que está como override, como dice nuestro compañero la única forma de que no ocurra es usando new, pero entonces tampoco tendrias que declarar la funcion de arriba como virtual, sería una tontería.

una vez se ha acabado el super(); se llama a la propia constructora de Derivada y como podeis ver se ejecuta normalmente.

De C# tengo que investigar más, es un lenguaje que me interesa, pero donde esté C++ que se kiten las "mariconadas" de los formularios jeje, sólo que le cogí rabia al visual basic, pero weno C# es otro mundo ^^ aunque se ejecute sobre el mismo framework grrr


Vicente

 Hola,

Citar
¡De C# tengo que investigar más, es un lenguaje que me interesa, pero donde esté C++ que se kiten las "mariconadas" de los formularios jeje, sólo que le cogí rabia al visual basic, pero weno C# es otro mundo ^^ aunque se ejecute sobre el mismo framework grrr

C# es a Java lo que Visual Basic.NET a Visual Basic 6: una forma de conseguir que esos grupos de programadores se pasen a .NET. Son casi iguales, no hay muchas diferencias de cosas que puedas hacer en uno si y en otro no y en la versión 2005 unas cuantas han desaparecido. Un saludo!

Vicente

CoLSoN2

 Gallifante para Vicente, que ha resuelto el enigma XD
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

Grugnorr

 Vicente, dices que C# y Java sonm iguales... y que VB.NET y VB6 son iguales?, o hablas de C# y VB.NET?


PD: VB.NET y VB6 tienen en común tanto como AnsiC y Java  
hat the hells!






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.