Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





C# override, llamadas a metodos base y algo de inline

Iniciado por TrOnTxU, 29 de Abril de 2011, 11:05:40 AM

« anterior - próximo »

TrOnTxU

Hola!

Sigo trasteando con C# y la maquina Mono (para scripting de entidades como siempre). Y me han vuelto a salir problemillas :)

1) Primero como llamar a un metodo sobreescrito de una clase base que no sea directamente la del "padre" de la clase, sino más bien el "abuelo", y me explico con un ejemplo tonto.
En C++ haria algo asi (lo escribo sobre la marcha, la implementacion en el mismo archivo que la declaracion y sin comprobar) pero la idea es esta:

class ClaseA {
  public:
    virtual void Escribe() { printf("Hola soy clase A\n"); }
}

class ClaseB : public ClaseA {
  public:
    virtual void Escribe() { printf("Hola soy clase B\n"); }
}

class ClaseC : public ClaseB {
  public:
    virtual void Escribe() {
      printf("Hola soy clase C\n");
      printf("Ahora muestro la clase base:\n");
      ClaseA::Escribe();
    }
}


El rollo es que ClaseC claseC(); claseC.Escribe(); esto deberia mostrar:
Hola soy clase C
Ahora muestro la clase base:
Hola soy clase A


Si esto lo hago en C#

public class ClaseA {
public virtual void Escribe() { Console.WriteLine("Hola soy clase A"); }
}

public class ClaseB : ClaseA {
public override void Escribe() { Console.WriteLine("Hola soy clase B"); }
}

class ClaseC : ClaseB {
public override void Escribe() {
Console.WriteLine("Hola soy clase C");
Console.WriteLine("Ahora muestro la clase base:");
base.Escribe();
}
}


Para el siguiente código de ejecucion:
ClaseA ca = new ClaseA();ca.Escribe();
ClaseC cc = new ClaseC(); cc.Escribe();


Obtengo la salida:
Hola soy clase A
Hola soy clase C
Ahora muestro la clase base:
Hola soy clase B


Con lo que mi problema es poder llamar al metodo "ClaseA.Escribe" desde "ClaseC.Escribe", sin pasar por el metodo "ClaseB.Escribe".
Supongo que será una chorrada como un piano pero mira que he "googleado" y no encontrado ningun ejemplo, lo más chungo es que me parece que alguna vez lo tengo que haber echo ya, pero ahora no caigo si se puede y como.


2) Y paso a la segunda pregunta chorra. Esto viene del tema de optimizaciones del compilador, y sobretodo el tema de metodos inline.
Para liar más la cosa ahora compilo las DLL de los scripts con el compilador de MonoDevelop, aunque no tendria que haber problemas en que lo compilara con el VS y luego copiar la DLL al directorio de ejecucion.

En C# no hay ninguna "palabra reservada" que inidique al compilador tu preferencia por hacer inline de una funcion, aunque por el contrario si que puedes darle un atributo a un metodo para indicarle que "no haga nunca un inline" de dicho metodo.
En un principio un metodo que ocupe menos de x bytes de codigo IL (creo que son 32) y no se llame recursivamente es firme candidato para este tipo de optimizacion.
Vale, hasta aqui todo OK. Mi problema actual: los arboles de comportamiento que he implementado para la IA utilizan un "yield return [Xe2BTResult]" como "retorno" de un metodo "IEnumerable<Xe2BTResult>", con lo que utlizo los iteradores para "simular" código de corrutinas.
Pero si quiero trazar el arbol (ver en tiempo de ejecucion que nodos estan activos y en que estado), debo guardar la informacion de estado de la "corrutina" con lo que hago esto: yield return Trazar([Xe2BTResult]). Donde Xe2BTResult es un enum y Trazar es un metodo "no estatico" de la clase nodo con una implementacion parecida a esto:
Xe2BTResult Trazar(Xe2BTResult  result)
{
#ifdef DEBUG
    m_blackboard.ChangeState(m_nodeID, result);
#endif
    return result;
}


En C++ si quisiera que en "Release" se obviara el registro del estado en la blackboard y el "jump" a la funcion implementaria Trazar como un macro "#define" sin pensarmelo dos veces.
Mi problema es saber si "Trazar" (al menos en Release cuando el #ifdef no se cumple) es candidato a optimizar mediante inline de forma que el código quedara de la siguiente manera:
En DEBUG: yield return Trazar([Xe2BTResult]); (o con toda la funcion Trazar "inlineada")
Y en RELEASE: yield return [Xe2BTResult];

Supongo que me puedo poner a decompilar IL y examinar el codigo resultante pero me gustaria que si alguien tiene experiencia con esto me comentara su opinion.
Tanto para compilador .NET como Mono.

Como alternativa he leido que algunos "Delegates" o funciones anonimas o algo asi, en C# se "desplegan" en tiempo de compilacion como funciones "inline" con lo que sustituyendo el metodo "Trazar" por algun tipo de funcion anonima podia conseguir el mismo resultado que con un "#define" o un "inline" de C++.
¿Alguna ayudita u opinión al respecto? (por cierto, me gustaria no pasar de 2.0, por lo que supongo que "Action" no funcionaria).


3) Y por fin la ultima pregunta.
En consolas Next-Gen (si suponemos la 360 y la PS3) se acabo el "codigo dinámico", todo codigo nativo no firmado no se puede ejecutar.
La maquina de .NET en un principio (y segun tengo entendido) genera "codigo maquina nativo" sobre a marcha (conforme se necesitan) a partir del codigo IL.
En XNA supongo que la solución es generar ese "codigo nativo" en el proceso de compilacion, en vez de "conforme se necesita". Y por supuesto eliminar el JIT de las features del lenguaje.
Si tengo que utilizar la maquina Mono en estas consolas:
¿la solucion que he propuesto seria la correcta?
¿opiniones al respecto?
¿alguna ayudita XD?


Bueno pues muchas gracias por anticipado, y perdonar por los "tostones" de post que pongo (ya vienen siendo habituales en mi).

ADEW!!
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

blau

No estoy seguro del todo, pero vicente te respondera mejor.

1) (cc as ClaseA).Escribe   

2) npi

TrOnTxU

Cita de: blau en 29 de Abril de 2011, 11:55:12 AM

1) (cc as ClaseA).Escribe   


Hola, gracias por contestar. Igual es por el tema de la maquina Mono (y puede que en .NET funcione de manera diferente, aunque creo que no), pero un código del estilo:

class ClaseC : ClaseB {
public override void Escribe() {
Console.WriteLine("Hola soy clase C\n");
Console.WriteLine("Ahora muestro la clase base:");
// -- Comentado ==> base.Escribe();
                (this as ClaseA).Escribe();
}
}


Entraria en un bucle infinito de recursion, ya que aunque indiques el casting dinamico a clase A, la informacion del objeto sigue indicando que es un objeto de claseC con lo que se llamaria a ClaseC.Escribe, en vez de a ClaseA.Escribe.

Realmente es lo mismo que tener un array de "ClaseA"s, con objetos de claseA, claseB y claseC. Aunque la referencia sea de la clase base, al llamar a "Escribe" se utilizaria la tabla de funciones virtuales y se llamaria "en teoria" al metodo correspondiente a cada clase.


De todas muchas gracias :)
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

blau

pues la otra opción que se me ocurre es con algo asi:

base.base.Escribe()

EDIT: No lo permite el compilador, al final me he puesto a probarlo en visual studio :(

TrOnTxU

Cita de: blau en 29 de Abril de 2011, 12:06:01 PM
pues la otra opción que se me ocurre es con algo asi:

base.base.Escribe()

XD XD XD XD

Lo mismo he probado yo, y el MonoDevelop me dice que nainai, que el keyword base no se puede utilizar de esa manera.

Es curioso porque la dos contestaciones que has dado son las primeras que he probado y en el mismo orden XD XD

Gracias por la ayuda igualmente, tendremos que esperar a que Vicente nos resuleva los problemas como siempre XD

Un saludo.
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

blau

Cita de: TrOnTxU en 29 de Abril de 2011, 12:08:27 PM
Cita de: blau en 29 de Abril de 2011, 12:06:01 PM
pues la otra opción que se me ocurre es con algo asi:

base.base.Escribe()

XD XD XD XD

Lo mismo he probado yo, y el MonoDevelop me dice que nainai, que el keyword base no se puede utilizar de esa manera.

Es curioso porque la dos contestaciones que has dado son las primeras que he probado y en el mismo orden XD XD

Gracias por la ayuda igualmente, tendremos que esperar a que Vicente nos resuleva los problemas como siempre XD

Un saludo.

:)


blau

Por lo que leo por ahí simplemente no se puede, solo se puede acceder con "base" al método sobrecargado del padre.

Yo he probado hasta por reflexión y no hay manera.

typeof(AA).GetMethod("Escribe").Invoke(this, null);

EDIT: Por lo visto si se puede, pero hace falta una libreria que aporta el metodo GetNonVirtualnvoker:

class CC : BB
    {
        private static readonly Func<A, string> baseBaseEscribe = typeof(AA).GetNonVirtualInvoker<Func<AA, void>>("Escribe");
        public override void Escribe () { baseBaseEscribe(); }
    }

La libreria e informacion aqui: http://kennethxu.blogspot.com/2009/05/strong-typed-high-performance_18.html

Vicente

1) No se puede. Lo unico que he encontrado es emitiendo IL en el mismo link que te ha puesto blau. Si estas usando mono, la libreria Mono.Cecil es bastante mas facil de usar que Reflection.Emit segun tengo entendido. Pero en ambos casos es una movida del quince.

2) Con el inline y tal te estas metiendo en terreno farragoso. Ademas Mono y .NET tienen normas totalmente diferentes respecto a cuando deciden hacer inline. Aun asi, un solo return deberia inlinearse (aunque no me apostaria nada :p).

Para el tipo de cosas de debug y release, .NET tiene un mecanismo que a mi me gusta mucho mas que es el atributo ConditionalAttribute.

http://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute.aspx

Si marcas un metodo como [Conditional("DEBUG")] cuando compiles en release ese metodo no existira ni sus llamadas a el claro esta :) Yo usaria eso en vez de los defines en el trazado.

Lo del delegado anonimo no acabo de entenderte :S

3) Hay una herramienta llamada Ngen que compila IL to codigo nativo. En Mono se puede hacer lo mismo, por aqui explican como:

http://www.mono-project.com/Mono:Runtime

Sobre que haran en las consolas, ni idea :D

TrOnTxU

Muchas gracias a los dos   :)

Seguiré investigando  ;)

En cuanto a consolas he visto que se rula Mono en PS3 pero bajo linux :S

Ya os cuento algo cuando consiga que rule en las consolas

ADEW!!
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!


TrOnTxU

Cita de: Vicente en 29 de Abril de 2011, 03:18:20 PM
Hay juegos de verdad de PS3 que usan Mono ;)

:) si, además Unity3D 3 rula mono tb en ps3.

Para la Wii, Novell si que tiene una maquina Mono.
Para PS3 pone esto: http://www.mono-project.com/Mono:PlayStation3

Tendré que mirar cuando pueda la informacion en scedev, y en los foros, sobre como pirula en CellOS, y a ver que licencia tiene Novell para PS3.

De todas formas casi seguro que acabemos usando Unity3D para este proyecto, o sea que una cosa menos de la que tendré que preocuparme :D



Por cierto, al final he pasado de comerme la cabeza con la llamada al metdodo de la clase base y he reescrito un poco de código para que la implementación funcione sin necesidad de saltarse un escalon en la jerarquia.
En cuanto al inline voy a confiar ciegamente de momento en el compilador, más adelante ya decompilare IL y me aseguraré de que funcione todo como debe.

Bueno, gracias de nuevo.

ADEW!

Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

TrOnTxU

Cita de: blau en 29 de Abril de 2011, 12:33:50 PM

La libreria e informacion aqui: http://kennethxu.blogspot.com/2009/05/strong-typed-high-performance_18.html


He estado investigando los links y hay info muy curiosa para emitir codigo IL.
No lo voy a utilizar para la llamada al metodo del "grandparent", pero ya tengo en mente alguna aplicacion para esto  :D

Gracias blau  ;)
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

Vicente

Aunque no sea emitir IL directamente, hay ciertas cosas que antes se hacian emitiendo IL (o con reflexion) que ahora se pueden hacer mas bonitas con arboles de expresiones.

http://msdn.microsoft.com/en-us/library/bb397951.aspx

Espero que en C# vNext mejoren esto aun mas con Roslyn (abrir el compilador de arriba a abajo para los desarrolladores).






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.