Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I retrieve the interface type within an interface method?

How can I determine the underlying interface from which the method DoSomething() is called? Additional question: Can I already determine the underlying interface in the MyClass constructor? I assume not as it is not known at instantiation time, correct?

Edit: I am not looking for explicit interface implementations but a different way to determine the underlying interface.

public interface ITest
{
    void DoSomething();
    //....more methods
}

public interface IDecoy
{
    void DoSomething();
    //...more methods
}

public class MyClass : ITest, IDecoy
{
    public void DoSomething()
    {
        //Question: How can I determine the underlying interface that called this method?
        //at one time it is ITest, at another IDecoy. How can I figure out which one at each time?
    }
}

public class Test
{
    public Test()
    {
        ITest myClassInstance1 = new MyClass();
        IDecoy myClassInstance2 = new MyClass();

        myClassInstance1.DoSomething();
        myClassInstance2.DoSomething();
    }
}
like image 829
Matt Avatar asked Jul 10 '26 07:07

Matt


2 Answers

public class MyClass : ITest, IDecoy
{
    void ITest.DoSomething()
    {
        //called with ITest 
    }
    void IDecoy.DoSomething()
    {
        //called with IDecoy 
    }
}
like image 74
Arsen Mkrtchyan Avatar answered Jul 11 '26 21:07

Arsen Mkrtchyan


This question is quite intriguing, and so I set out to see whether it was possible to find a way of identifying which interface was used without the solution already suggested.

The solution I am using essentially finds the metadata token of the method that was called in the IL code of the caller and looks this up against the metadata token of the DoSomething method from each interface:

public void DoSomething()
{
    StackFrame CallerFrame;
    StackTrace CallStack;
    int CodeOffset;
    MethodBody MethodBody;
    int MethodToken;
    int TokenIDecoy;
    int TokenITest;

    // Get the metadata tokens for both interface methods
    TokenIDecoy = Type.GetType("Proto.SO18203446.IDecoy").GetMethod("DoSomething").MetadataToken;
    TokenITest = Type.GetType("Proto.SO18203446.ITest").GetMethod("DoSomething").MetadataToken;

    // Get the caller
    CallStack = new StackTrace();
    CallerFrame = CallStack.GetFrame(1);

    // Get the metadata token called by the IL
    CodeOffset = CallerFrame.GetILOffset();
    MethodBody = CallerFrame.GetMethod().GetMethodBody();
    MethodToken = BitConverter.ToInt32(MethodBody.GetILAsByteArray(), CodeOffset - 4);

    // Check to see which interface was used
    if (MethodToken == TokenIDecoy)
        Console.WriteLine("IDecoy was called");
    else if (MethodToken == TokenITest)
        Console.WriteLine("ITest was called");
    else
        Console.WriteLine("Not sure what happened here");
}

Remember to change the GetType call parameter to be your own namespace (so mine is Proto.SO18203446 but yours is very likely to be somehting else).

The steps of the process are simple:

  • Lookup the metadata tokens for each interface DoSomething method
  • Find the calling frame (the one that makes the call to DoSomething)
  • Lookup the IL offset of the call to the DoSomething method and extract the metadata token for it
  • Compare the called metadata token to the token for each DoSomething in the interfaces

I would like to add that I am not recommending or endorsing this code - it is more to prove that it is possible to achieve your aim from an academic perspective.

like image 35
Martin Avatar answered Jul 11 '26 21:07

Martin