2010-10-01 18 views
5

¿Es posible reflejar una implementación de interfaz explícita desde la pila de llamadas? Quiero usar esta información para buscar un atributo en la interfaz.¿Cómo se refleja en la implementación explícita de la interfaz C# desde la pila de llamadas?

Teniendo en cuenta este código:

interface IFoo 
{ 
    void Test();  
} 

class Foo : IFoo 
{ 
    void IFoo.Test() { Program.Trace(); } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     IFoo f = new Foo(); 
     f.Test(); 
    } 

    public static void Trace() 
    { 
     var method = new StackTrace(1, false).GetFrame(0).GetMethod(); 
     // method.??? 
    } 
} 

En concreto, en Trace(), me gustaría ser capaz de obtener a partir de typeof(IFoo)method.

En la ventana del reloj, si miro method.ToString() me da Void InterfaceReflection.IFoo.Test() (InterfaceReflection es el nombre de mi ensamblaje).

¿Cómo puedo llegar al typeof(IFoo) desde allí? ¿Debo usar una búsqueda de tipo basada en el nombre del ensamblado en sí, o hay un Type IFoo escondido en algún lugar en el MethodBase?

ACTUALIZACIÓN:

Aquí está la solución final, gracias a Kyte

public static void Trace() 
{ 
    var method = new StackTrace(1, false).GetFrame(0).GetMethod(); 
    var parts = method.Name.Split('.'); 
    var iname = parts[parts.Length - 2]; 
    var itype = method.DeclaringType.GetInterface(iname); 
} 

itype tendrán el tipo de interfaz para el método de aplicación. Esto solo funcionará con implementaciones de interfaz explícitas, pero eso es exactamente lo que necesito. Ahora puedo usar itype para consultar los atributos asociados al tipo de interfaz real.

Gracias a todos por su ayuda.

Respuesta

3

Probando con VS2010, encontré DeclaringType, que obtiene el tipo de objeto que contiene el método, desde donde puede obtener las interfaces como objetos Type.

public static void Trace() { 
     var stack = new StackTrace(1, true); 
     var frame = stack.GetFrame(0); 
     var method = frame.GetMethod(); 

     var type = method.DeclaringType; 

     Console.WriteLine(type); 
     foreach (var i in type.GetInterfaces()) { 
      Console.WriteLine(i); 
     } 
    } 

Devuelve:

TestConsole.Foo 
TestConsole.IFoo 

(Me llaman el proyecto TestConsole)

+0

¡Ah! Eso es todo, gracias. Solo estaba buscando propiedades, y no pensé en buscar métodos. Creo que 'GetInterfaces()' es lo que puedo usar. Usaré 'method.Name' que me da' InterfaceReflection.IFoo.Test', sacaré el 'IFoo' y usar 'GetInterface()' para encontrar su tipo coincidente. Eso funciona. – scobi

+0

Si Foo implementó más de 1 interfaz, entonces aún tendría que averiguar a cuál pertenece el método ... Además, ¿qué pasa si la prueba no se definió en la interfaz, y era un método real de Foo? – CodingWithSpike

+0

@ rally25rs: Puede ejecutar i.GetMethods() y hacer coincidir el resultado de GetFrame (0) .GetMethod() – Kyte

0

No quiero presumir demasiado, pero en este caso, parece que puede estar causando cierta confusión porque Foo y Program son interdependientes. Por lo general, creo que Program podría "ser dueño" de Foo (que sería agnóstico del Programa) de tal forma que es responsable de configurar al delegado para que el reflejo pueda evitarse ... la forma en que lo tiene configurado, Foo "posee "(de hecho, supongo que depende de que sea probablemente más preciso) Programa de alguna manera (porque es hardcoing una llamada a su Program.Trace()), y Program" posee "Foo de una manera (porque controla la instancia).

No sé si esto funcionaría en su entorno particular, pero parece que una operación de tipo evento podría tener más sentido y manejar la comunicación más simple.

ETA: Ejemplo de código:

public interface IFoo 
{ 
    event EventHandler Testing; 
    void Test(); 
} 
public class Foo : IFoo 
{ 
    public event EventHandler Testing; 
    protected void OnTesting(EventArgs e) 
    { 
     if (Testing != null) 
      Testing(this, e); 
    } 
    public void Test() 
    { 
     OnTesting(EventArgs.Empty); 
    } 
} 

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     IFoo f = new Foo(); 
     f.Testing += new EventHandler(f_Testing); 
     f.Test(); 
    } 

    static void f_Testing(object sender, EventArgs e) 
    { 
     IFoo foo = sender as IFoo; 
     if (foo != null) 
     { 
      //... 
     } 
    } 
} 

yo podría ser malentendido su pregunta, sin embargo.

+0

Definitivamente estás malentendido mi pregunta. :) Este es un ejemplo artificial con el único propósito de hacer la pregunta de la manera más simple posible. Realmente estoy interesado en (a) cómo obtener el reflejo correcto y (b) de qué se trata este asunto de InterfaceReflection.exe. Esta pregunta no es sobre diseño arquitectónico. – scobi

2

method será System.Reflection.RuntimeMethodInfo, que es una clase derivada de System.Reflect.MethodBase. Podría, por ejemplo, llama al Invoke() (aunque si lo hiciste en el punto donde lo obtuviste, entonces esto dará como resultado una recursión infinita que finalmente muere al desbordar la pila).

Llamar a ToString() en él devuelve un nombre completo. ¿Llamaste al proyecto InterfaceReflection?

No estoy seguro de qué más quieres que eso.

Editar: De acuerdo, ahora sí.Para encontrar el tipo de declaración mira en la propiedad DeclaringType, esto devolverá la clase en la que se declaró el método (que podría ser la clase a la que se llamó, o una clase base):

Hasta ahora, tan fácil, esto vuelve un objeto Type para Foo.

Ahora, por el truco, porque te importa la interfaz en la que fue declarado. Sin embargo, podría haber más de una interfaz que definiera un método con exactamente la misma firma, lo que significa la simple pregunta "si esto provino de una interfaz, ¿qué era esa interfaz?" no siempre tiene una sola respuesta.

Puede haber una manera más limpia de hacer esto, pero lo único que ocurre es llamar GetInterfaces() en el objeto Type que obtuvo de DeclaringType, y luego en busca de uno cuyo nombre coincide con la firma del método.

+0

Wow. Pie en boca. De hecho, llamé al proyecto InterfaceReflection y lo olvidé. Estaba pensando que era un nombre .NET interno. Ok, actualizaré la pregunta. Todavía tengo el problema de encontrar el tipo de interfaz correcto. – scobi

+0

@Scott Bilas okily, he actualizado mi respuesta en consecuencia. –

0

Creo .NET añade el nombre completo de la parte delantera de la propiedad MethodInfo.Name de modo que tenga un único nombre para cada método Piense:

interface IFoo 
{ 
    void Test(); 
} 
interface IFoo2 
{ 
    void Test(); 
} 

class Foo : IFoo, IFoo2 
{ 
    void IFoo.Test() { Trace(); } 
    void IFoo2.Test() { Trace(); } 
} 

En este caso, typeof(Foo).GetMethods()Test() volvería ambos métodos, pero sus nombres entraría en conflicto, así que supongo que anexan el nombre de la interfaz para que sean únicos?

El MethodInfo.DeclaringType devuelve el tipo que contiene la implementación. Entonces, si IFoo fuera en realidad un tipo base en lugar de una interfaz, y hubiera una declaración de método base allí, entonces .DeclaringType devolvería el tipo de la clase base.

Curiosamente, me parece que no puede encontrar el nombre de la interfaz real en cualquier parte del MethodInfo tampoco, así que supongo que tendría que mirar hacia arriba por su nombre, algo así como:

public static void Trace() 
    { 
     var method = new System.Diagnostics.StackTrace(1, false).GetFrame(0).GetMethod(); 
     var fromType = method.DeclaringType; 
     if (method.Name.Contains(".")) 
     { 
      var iname = method.Name.Substring(0, method.Name.LastIndexOf('.')); 
      fromType = Type.GetType(iname); // fromType is now IFoo. 
     } 
    } 
Cuestiones relacionadas