Foros - Stratos

Programadores => General Programadores => Mensaje iniciado por: CoLSoN2 en 10 de Noviembre de 2004, 07:40:30 PM

Título: Una Dudilla Sobre C#
Publicado por: CoLSoN2 en 10 de Noviembre de 2004, 07:40:30 PM
 Ú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í?
Título: Una Dudilla Sobre C#
Publicado por: _Grey en 10 de Noviembre de 2004, 11:14:30 PM
 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++)
Título: Una Dudilla Sobre C#
Publicado por: BeRSeRKeR en 10 de Noviembre de 2004, 11:56:45 PM
 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.
Título: Una Dudilla Sobre C#
Publicado por: CoLSoN2 en 11 de Noviembre de 2004, 09:20:18 AM
 
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
Título: Una Dudilla Sobre C#
Publicado por: CoLSoN2 en 14 de Noviembre de 2004, 06:20:37 PM
 ¿Entonces nadie sabe nada sobre porqué lo han hecho así?
Título: Una Dudilla Sobre C#
Publicado por: Buffon en 16 de Noviembre de 2004, 05:54:56 PM
 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?
Título: Una Dudilla Sobre C#
Publicado por: Buffon en 16 de Noviembre de 2004, 06:04:29 PM
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".

;)
Título: Una Dudilla Sobre C#
Publicado por: CoLSoN2 en 16 de Noviembre de 2004, 07:37:03 PM
 @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.
Título: Una Dudilla Sobre C#
Publicado por: Ffelagund en 16 de Noviembre de 2004, 07:38:02 PM
 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é?
Título: Una Dudilla Sobre C#
Publicado por: synchrnzr en 16 de Noviembre de 2004, 08:02:33 PM
 De acuerdo con Ffelagund. Ahí tiene que fallar algo. Da ganas de abrir el C# y probar :ph34r:

sync
Título: Una Dudilla Sobre C#
Publicado por: Vicente en 16 de Noviembre de 2004, 09:03:56 PM
 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.
Título: Una Dudilla Sobre C#
Publicado por: Buffon en 16 de Noviembre de 2004, 09:16:00 PM
 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

Título: Una Dudilla Sobre C#
Publicado por: Vicente en 16 de Noviembre de 2004, 09:39:51 PM
 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
Título: Una Dudilla Sobre C#
Publicado por: CoLSoN2 en 16 de Noviembre de 2004, 10:31:58 PM
 Gallifante para Vicente, que ha resuelto el enigma XD
Título: Una Dudilla Sobre C#
Publicado por: Grugnorr en 16 de Noviembre de 2004, 11:04:28 PM
 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  
Título: Una Dudilla Sobre C#
Publicado por: Vicente en 17 de Noviembre de 2004, 01:39:50 AM
 Hola,

lo mismo no me he explicado muy bien: me refería a que C# y VB.NET son muy parecidos respecto a capacidades (trabajan sobre el mismo framework) y que las diferencias de lenguaje las van igualando poco a poco. Y además tienen objetivos muy parecidos: el de C# es atraer a los programadores Java a .NET y el de VB.NET atraer a los programadores VB6 a .NET. A ver si así queda más claro ;) Un saludo,

Vicente
Título: Una Dudilla Sobre C#
Publicado por: Grugnorr en 17 de Noviembre de 2004, 07:39:39 AM
 Así queda mucho más claro y correcto :)


PD: De todas formas ... la sintaxis de VB sux