简体   繁体   中英

How to reflect on C# explicit interface implementation from the call stack?

Is it possible to reflect on an explicit interface implementation from the call stack? I want to use this info to look up an attribute on the interface itself.

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

Specifically, in Trace(), I would like to be able to get to typeof(IFoo) from method .

In the watch window, if I look at method.ToString() it gives me Void InterfaceReflection.IFoo.Test() (InterfaceReflection is the name of my assembly).

How can I get to typeof(IFoo) from there? Must I use a name-based type lookup from the assembly itself, or is there a Type IFoo hidden somewhere in the MethodBase ?

UPDATE:

Here's the final solution, thanks to 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 will have the interface type for the implementing method. This will only work with explicit interface implementations, but that's exactly what I need. Now I can use itype to query attributes attached to the actual interface type.

Thanks to everyone for their help.

Testing around with VS2010, I found DeclaringType, which gets the object type that contains the method, from where you can get the interfaces as Type objects.

    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);
        }
    }

Returns:

TestConsole.Foo
TestConsole.IFoo

(I called the project TestConsole)

method will be a System.Reflection.RuntimeMethodInfo , which is a class derived from System.Reflect.MethodBase . You could eg call Invoke() on it (though if you did so at the point where you obtained it, then this is going to result in an infinite recursion that eventually dies by overflowing the stack).

Calling ToString() on it returns a fully qualified name. Did you call the project InterfaceReflection?

Not sure what more you want than that.

Edit: Okay, now I do. To find the declaring type look at the DeclaringType property, this will return the class on which the method was declared (which could be the class it was called on, or a base class):

So far so easy, this returns a Type object for Foo.

Now for the tricky bit, because you care about the interface it was declared on. However, there could be more than one interface that defined a method with precisely the same signature, which means the simple question "if this came from an interface, what was that interface?" doesn't always have a single answer.

There may be a neater way to do this, but all I can think of is calling GetInterfaces() on the Type object you got from DeclaringType , and then looking for one whose name matches the method's signature.

I don't want to presume too much, but in this case, it looks like you may be causing some confusion because Foo and Program are inter-dependent. Typically, I would think Program would "own" Foo (which would be agnostic of Program) in such a way that it's responsible for setting the delegate so reflection could likely be avoided...the way you have it set up, Foo "owns" (actually, I guess depends on is probably more accurate) Program in a way (because it's hardcoing aa call to its Program.Trace() ), and Program "owns" Foo in a way (because it controls the instance).

I don't know if this would work in your particular scenerio, but it looks like an event type operation might make more sense and handle the communication more simply.

ETA: Code sample:

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

I might be misunderstanding your question, though.

I think .NET appends the full name to the front of the MethodInfo.Name property so that it has a unique name for each method. Think of:

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

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

In this case, typeof(Foo).GetMethods() would return both Test() methods but their names would conflict, so I guess they appended the interface name to make them unique?

The MethodInfo.DeclaringType returns the type that contains the implementation. So if IFoo were actually some base type instead of an interface, and there was a base method declaration there, then .DeclaringType would return the type of the base class.

Interestingly, I can't seem to find the actual interface name anywhere in the MethodInfo either, so I guess you would have to look it up by name, something like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM