2012-04-26 9 views
6

tengo estas clases:C# opciones de envío múltiple?

class Asset 
{ } 

class House:Asset 
{ } 

Considere estos foráneos funciones estáticas:

static void Foo (Asset a) { } 
static void Foo (House h) { } 

si escribo:

House h = new House (...); 
Foo(h); 

se llamará Foo(House) (compilar vinculante tiempo)

si escribo:

Asset a = new House (...); 
Foo(a); 

se llamará Foo(Asset) (compilar vinculante tiempo)

objetivo: acceder al método de tipo en tiempo de ejecución:

tengo 2 opciones:

1) usando dinámica de esta manera:

Asset a = new House (...); 
Foo ((dynamic)a); // NOW it will call Foo(House) 

2) mueve las funciones de static a override usando polymorphism mechanism.

pregunta:

¿hay alguna otra forma de hacerlo (sin mover las funciones de polymorphism mechanism || dynamic)?

+0

Asset a = new House (...); no llamará a Foo (activo), porque * el tipo de tiempo de ejecución * es House, por lo que llamará a Foo (House) – Tigran

+6

@Tigran: la resolución de sobrecarga se realiza en tiempo de compilación. 'Asset a = nueva Casa (...); Foo (a); 'llamará a' Foo (Asset) 'porque' a' se declara como 'Asset'. – dtb

+0

@dtb: no hay ninguna * sobrecarga * estos son métodos estáticos, y * no * llamará a Foo (activo), e incluso si existe el * significado * de sobrecarga es la identificación del tipo de tiempo de ejecución, y el tipo de tiempo de ejecución en la cadena Asset a = new House() es House. – Tigran

Respuesta

9

objetivo: acceder al método de tipo de tiempo de ejecución

Eso es lo que la palabra clave dynamic está ahí para. En realidad, es una forma muy simple de & de hacer múltiples despachos.

Sus opciones finales para Multiple Dispatch son

  1. dynamic
  2. dobles métodos virtuales de despacho
  3. Algunos HASHED colección de reglas función anónima
  4. if (x is House) ... else if(x is Asset)...
  5. Reflexión - muy lento y feo

pregunta: ¿hay alguna otra forma de hacerlo (sin mover las funciones al mecanismo de polimorfismo || dinámico)?

Así que sí, hay formas de hacer que requieren de mucho trabajo de su parte cuando uno podía utilizar dynamic que es rápido, menos propenso a errores, y realmente clean syntax wise.

+0

Un buen compilación de posibles soluciones ... Gracias .. Tengo una solución similar mencionada en http://stackoverflow.com/questions/21261519/net-4-0-optimized-code-for-refactoring-existing-if-conditions-and- is-operat/21284844 # 21284844 – Lijo

1

creo que esto es lo que está sucediendo bajo el capó de Foo((dynamic)a):

Asset a = new House(); 

Type t = typeof(MainClass); 
t.InvokeMember("Foo", 
    System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { a }); 

que resolverá a Foo(House h)


Un viaje rápido a monodis.exe, sin necesidad de utilizar la reflexión (por ejemplo InvokeMember) , es decir, utilizando la palabra clave dinámica en su lugar, Asset a = new House(); Foo((dynamic)a), esta es la IL:

IL_0025: ldstr "Foo" 
IL_002a: ldnull 
IL_002b: ldtoken MainClass 
IL_0030: call class [mscorlib]System.Type class [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 

Más o menos lo que es su corazonada le dirá, el "Foo" es un regalo muerto que la dinámica es reflexión-y tipo de negocio.

Ahora bien, esto es sans dinámicos, es decir Asset a = new House(); Foo(a):

IL_0010: ldloc.0 
IL_0011: call void class MainClass::Foo(class Asset) 

La instrucción quemado es más o menos decidido, no va a cambiar, siempre se resuelven a Foo(Asset);

Aquí está el código completo se puede utilizar para analizar el comportamiento dinámico (a través de monodis.exe o ildasm.exe):

using System; 

public class MainClass { 
    public static void Main() { 

     Console.WriteLine("Hei"); 

     Asset a = new House();   
     Foo(a);  
     Foo((dynamic)a); 

     object x = 7; 
     Foo((dynamic)x); 
    } 

    public static void Foo(House h) { Console.WriteLine("House"); } 
    public static void Foo(Asset a) { Console.WriteLine("Asset"); } 
    public static void Foo(int i) { Console.WriteLine("int"); } 
} 


public class Asset { 
} 

public class House : Asset { 
} 

de salida:

Hei 
Asset 
House 
int 

Esto invocará el int sobrecarga Foo, es decir Foo(int i):

object x = 7; 
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { x }); 

Esto también lo haría:

t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { 8 }); 

Así que en su pregunta, ¿qué otra opción que puede utilizar, puede utilizar un método que acepta un objeto sin tipo:

public static void FooDynamic(object o) 
{ 
    Type t = typeof(MainClass); 
    t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { o }); 
} 

Invocar:

Asset a = new House(); 
FooDynamic(a); // will select Foo House overload 

int i = 7; 
FooDynamic(i); // will select Foo int overload 

También puede utilizar esta API para el código anterior: public static void Foo(object o), entonces usted tendría que llamar Foo así:

Asset a = new House(); 
Foo((object)a); // will resolve to House 

Teniendo en cuenta que ya hay una capacidad dynamic en C# 4, estaría en apuros para utilizar la reflexión, a menos que el dev sigue utilizando C# 3. Así que, utilice el enfoque dinámico en lugar :-)


ACTUALIZACIÓN

Por lo vale la pena, dynamic es lento (al menos en Mono), cuando ejecuto este código, hay un retraso considerable antes de que aparezca la letra "B", unos 2 segundos. La demora de la dinámica es reproducible incluso si cambio el orden de los códigos de dinámica y reflexión. El retraso de la reflexión es imperceptible, es más rápido que dinámico.

using System; 

public class MainClass { 

    public static void Main() { 

     // there's a delay on initial dynamic call, about two seconds 
     Test(); 
     Console.ReadLine(); 

     // dynamic's speed is instant on subsequent calls, 
     // as clarified by Eric Lippert, the delegate is cached, 
     // hence the elimination of delay on subsequent dynamic calls 
     Test(); 

    } 

    public static void Test() { 

     Asset a = new House(); 

     Console.WriteLine("A"); 
     Foo((dynamic)a); // there is a considerable delay here, the "B" string appears after two seconds 

     Console.WriteLine ("B");   
     Type t = typeof(MainClass); 
     t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { a }); 

     Console.WriteLine("C"); 

    } 


    public static void Foo(House h) { Console.WriteLine("House"); } 
    public static void Foo(Asset a) { Console.WriteLine("Asset"); } 
    public static void Foo(int i) { Console.WriteLine("int"); } 
} 


public class Asset { 
} 

public class House : Asset { 
} 
+0

Bajo el capó está haciendo cosas locas que el compilador hace para resolver métodos estáticos pero con el tipo de tiempo de ejecución compilando sobre la marcha y el almacenamiento en caché para su uso futuro. En realidad, su ejemplo de reflexión no funcionaría con métodos sobrecargados, faltando la carpeta utilizada para elegir la sobrecarga correcta. – jbtule

+0

au contrarie, funciona :-) InvokeMember se ocupa del negocio de qué método sobrecargado invocar. Ver mi edición –

+1

Lo siento, está en lo correcto, InvokeMember funciona en este caso, es más complicado en casos en los que no. Pero 'Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember', que es lo que el compilador realmente intercambia cuando usa el' dynamic' maneja más casos que ese método de reflexión, y emite IL en tiempo de ejecución para que a veces sea hasta 100 veces más rápido que reflexión. – jbtule

1

Si desea el punto de entrada estático, pero también desea el comportamiento polimórfico, la combinación más sencilla sería utilizar el patrón singleton. Le dará el punto de entrada estático, y el objeto que regrese tendrá métodos polimórficos.

También sugiero ignorar a todos los que dicen que un singleton es algo horrible y malo. Prima Donna singleton woe-prédica es un shibboleth de desarrolladores de nivel medio. Un singleton es solo una herramienta, y si se adapta a sus necesidades, entonces úselo.