2010-10-01 9 views
5

Est-il possible de réfléchir sur une implémentation d'interface explicite à partir de la pile d'appels? Je veux utiliser cette information pour rechercher un attribut sur l'interface elle-même.Comment réfléchir sur l'implémentation d'interface explicite C# à partir de la pile d'appels?

Donné ce code:

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.??? 
    } 
} 

Plus précisément, dans Trace(), je voudrais être en mesure d'obtenir à typeof(IFoo) de method.

Dans la fenêtre de la montre, si je regarde method.ToString() il me donne Void InterfaceReflection.IFoo.Test() (InterfaceReflection est le nom de mon assemblée). Comment puis-je obtenir typeof(IFoo) à partir de là? Dois-je utiliser une recherche de type basée sur le nom à partir de l'assemblage lui-même, ou y a-t-il un Type IFoo caché quelque part dans le MethodBase?

MISE À JOUR:

Voici la solution finale, grâce à 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 auront le type d'interface pour la méthode d'implémentation. Cela ne fonctionnera qu'avec des implémentations d'interface explicites, mais c'est exactement ce dont j'ai besoin. Maintenant, je peux utiliser itype pour interroger les attributs attachés au type d'interface réel.

Merci à tous pour votre aide.

Répondre

3

En testant avec VS2010, j'ai trouvé DeclaringType, qui obtient le type d'objet qui contient la méthode, d'où vous pouvez obtenir les interfaces en tant qu'objets 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); 
     } 
    } 

Retours:

TestConsole.Foo 
TestConsole.IFoo 

(j'ai appelé le projet TestConsole)

+0

Ah! C'est tout, merci. Je regardais seulement les propriétés, et je ne pensais pas à regarder les méthodes. Je pense 'GetInterfaces()' est ce que je peux utiliser. Je vais utiliser 'method.Name' qui me donne' InterfaceReflection.IFoo.Test', je vais sortir le "IFoo", et utiliser 'GetInterface()' pour trouver son type correspondant. Ça marche. – scobi

+0

Si Foo implémentait plus d'une interface, alors vous auriez encore à déterminer à laquelle la méthode appartient ... Plus que faire si Test n'était pas défini dans l'interface, et était une méthode réelle de Foo? – CodingWithSpike

+0

@ rally25rs: Vous pouvez exécuter i.GetMethods() et faire correspondre le résultat de GetFrame (0) .GetMethod() – Kyte

0

Je ne veux pas trop en supposer, mais dans ce cas, il semble que vous causiez une certaine confusion parce que Foo et Program sont interdépendants. Typiquement, je pense que le programme "posséderait" Foo (qui serait agnostique du programme) de telle sorte qu'il est responsable de la configuration du délégué afin que la réflexion puisse être évitée ... la façon dont vous l'avez configuré, Foo "possède "(en fait, je suppose que cela dépend probablement plus précis) Programmez d'une certaine manière (parce qu'il est difficile d'appeler son Program.Trace()), et le Programme" possède "Foo d'une certaine manière (parce qu'il contrôle l'instance). Je ne sais pas si cela fonctionnerait dans votre scenerio particulier, mais il semble qu'une opération de type événement pourrait avoir plus de sens et gérer la communication plus simplement.

ETA: Exemple de code:

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) 
     { 
      //... 
     } 
    } 
} 

je pourrais être malentendu à votre question, cependant.

+0

Vous êtes certainement malentendu ma question. :) Ceci est un exemple inventé uniquement dans le but de poser la question de la manière la plus simple possible. Je suis vraiment intéressé par (a) comment obtenir la bonne réflexion et (b) de quoi parle cette entreprise InterfaceReflection.exe. Cette question ne concerne pas la conception architecturale. – scobi

2

method sera un System.Reflection.RuntimeMethodInfo, qui est une classe dérivée de System.Reflect.MethodBase. Vous pourriez, par exemple, appelez le Invoke() dessus (bien que si vous l'avez fait au point où vous l'avez obtenu, alors cela se traduira par une récursion infinie qui finira par mourir en débordant la pile).

En appelant ToString() il renvoie un nom complet. Avez-vous appelé le projet InterfaceReflection?

Je ne sais pas ce que vous voulez de plus que cela.

Editer: D'accord, maintenant je le fais.Pour trouver le type déclaratif, regardez la propriété DeclaringType, cela renverra la classe sur laquelle la méthode a été déclarée (qui pourrait être la classe sur laquelle elle a été appelée ou une classe de base):

Jusqu'à présent, cela revient si facilement un objet Type pour Foo.

Maintenant, pour le peu difficile, parce que vous vous souciez de l'interface sur laquelle il a été déclaré. Cependant, il pourrait y avoir plus d'une interface qui définit une méthode avec exactement la même signature, ce qui signifie la question simple "si cela venait d'une interface, quelle était cette interface?" n'a pas toujours une seule réponse.

Il peut y avoir un moyen plus propre de le faire, mais tout ce que je peux penser à appeler est GetInterfaces() sur l'objet Type vous avez obtenu de DeclaringType, puis la recherche d'un dont le nom correspond à la signature de la méthode.

+0

Wow. Pied dans la bouche. J'ai en effet appelé le projet InterfaceReflection et je l'ai oublié. Je pensais que c'était un nom .NET interne. Ok, je vais mettre à jour la question. J'ai toujours le problème de trouver le bon type d'interface. – scobi

+0

@Scott Bilas okily, ont mis à jour ma réponse en conséquence. –

0

Je pense que .NET ajoute le nom complet à l'avant de la propriété MethodInfo.Name afin qu'il ait une expérience unique nom pour chaque méthode. Pensez à:

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

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

Dans ce cas, typeof(Foo).GetMethods() retourneraient les deux méthodes Test() mais leurs noms en conflit, donc je suppose qu'ils le nom ajoutés d'interface pour les rendre uniques? Le MethodInfo.DeclaringType renvoie le type qui contient l'implémentation. Donc, si IFoo était en fait un type de base au lieu d'une interface, et qu'il y avait une déclaration de méthode de base, alors .DeclaringType retournerait le type de la classe de base.

Fait intéressant, je ne peux pas sembler trouver le nom de l'interface réelle partout dans le MethodInfo soit, donc je suppose que vous auriez à le chercher par nom, quelque chose comme:

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. 
     } 
    }