22

I have a requirement where I need to know the name of the class (ApiController) which has a method (GetMethod) which is called by another method (OtherMethod) from a different class (OtherClass).

To help explain this, I hope the below pseudo-code snippets help.

ApiController.cs

public class ApiController
{
    public void GetMethod()
    {
        OtherMethod();
    }
}

OtherClass.cs

public class OtherClass()
{
    public void OtherMethod()
    {
        Console.WriteLine(/*I want to get the value 'ApiController' to print out*/)
    }
}

What I've tried:

  • I've looked at How can I find the method that called the current method? and the answers will get me the calling method (OtherMethod) but not the class (ApiController) which has that method
  • I tried [CallerMemberName] and using StackTrace properties but these don't get me the method's class name
Aleksey Solovey
  • 4,153
  • 3
  • 15
  • 34
Troy Poulter
  • 677
  • 1
  • 8
  • 29
  • [`StackFrame.GetMethod()`](https://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.getmethod(v=vs.110).aspx) might be useful to you. – ProgrammingLlama Jul 31 '18 at 05:36
  • 2
    If you know how to get the method, you also know which class has the method. The returned information isn’t just a name of a method, it’s all information of the method, including class – Sami Kuhmonen Jul 31 '18 at 05:39
  • How you call `GetMethod` method? means by creating instance of `ApiController` or by static reference? – er-sho Jul 31 '18 at 05:46
  • 3
    `new StackTrace().GetFrame(1).GetMethod().DeclaringType.FullName` – John Wu Jul 31 '18 at 05:54
  • As others have already stated, the `DeclaringType` [property](https://msdn.microsoft.com/en-us/library/system.reflection.memberinfo.declaringtype.aspx) is what you're looking for. – Hazel へいぜる Jul 31 '18 at 05:58
  • 5
    If you're just using this to print the name out, as in your example, then it's probably fine. If you're going to apply any *logic* based on the answer you get, I'd seriously suggest you revisit your design. Methods that magically change their behaviour based on the exact shape of their callstack are deeply confusing. (I.e. imagine what would happen if `ApiController` had some form of helper class and it moved the call to `OtherMethod` into the helper) – Damien_The_Unbeliever Jul 31 '18 at 06:04
  • 1
    Please give a [mcve] (next time). – JHBonarius Jul 31 '18 at 06:20

4 Answers4

22
using System.Diagnostics;

var className = new StackFrame(1).GetMethod().DeclaringType.Name;

Goes to the previous level of the Stack, finds the method, and gets the type from the method. This avoids you needing to create a full StackTrace, which is expensive.

You could use FullName if you want the fully qualified class name.

Edit: fringe cases (to highlight the issues raised in comments below)

  1. If compilation optimizations are enabled, the calling method may be inlined, so you may not get the value you expect. (Credit: Johnbot)
  2. async methods get compiled into a state machine, so again, you may not get what you expect. (Credit: Phil K)
Richardissimo
  • 5,596
  • 2
  • 18
  • 36
  • This works thank you! I appreciate the explanation in the other answers, especially [@kennyzx](https://stackoverflow.com/a/51606254/7788355) one as this explained what was happening in depth. But this answer, as you pointed out, appears to be the least expensive and gets the same result and should be a good reference for other looking to achieve the same thing. – Troy Poulter Jul 31 '18 at 06:28
  • 7
    This is not 100% reliable. If optimizations are turned on `GetMethod` may be inlined and not show up in the stack trace. – Johnbot Jul 31 '18 at 08:07
  • The stack contains the return address that, in general, may be different from the caller address. See https://stackoverflow.com/a/6597522/1846281 – Luca Cremonesi Aug 07 '18 at 21:13
11

So it can be done like this,

new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType.Name

StackFrame represents a method on the call stack, the index 1 gives you the frame that contains the immediate caller of the currently executed method, which is ApiController.GetMethod() in this example.

Now you have the frame, then you retrieve the MethodInfo of that frame by calling StackFrame.GetMethod(), and then you use the DeclaringType property of the MethodInfo to get the type in which the method is defined, which is ApiController.

kennyzx
  • 12,845
  • 6
  • 39
  • 83
  • 2
    just to avoid any confusion, the `GetMethod()` in your line has nothing to do with OP's `ApiController.GetMethod()`, right ? – Ciprian Tomoiagă Jul 31 '18 at 07:50
  • Yes, it is just a coincidence that OP coined a method called GetMethod() while StackFrame has this method too. – kennyzx Jul 31 '18 at 08:04
5

You can achieve this by below code

First you need to add namespace using System.Diagnostics;

public class OtherClass
{
    public void OtherMethod()
    {
        StackTrace stackTrace = new StackTrace();

        string callerClassName = stackTrace.GetFrame(1).GetMethod().DeclaringType.Name;
        string callerClassNameWithNamespace = stackTrace.GetFrame(1).GetMethod().DeclaringType.FullName;

        Console.WriteLine("This is the only name of your class:" + callerClassName);
        Console.WriteLine("This is the only name of your class with its namespace:" + callerClassNameWithNamespace);
    }
}

The instance of stackTrace is depend upon your implementation environment. you may defined it locally or globally

OR

You may use below method without creating StackTrace instance

public class OtherClass
{
    public void OtherMethod()
    {
        string callerClassName = new StackFrame(1).GetMethod().DeclaringType.Name;
        string callerClassNameWithNamespace = new StackFrame(1).GetMethod().DeclaringType.FullName;

        Console.WriteLine("This is the only name of your class:" + callerClassName);
        Console.WriteLine("This is the only name of your class with its namespace:" + callerClassNameWithNamespace);
    }
}

Try this may it help you

er-sho
  • 9,581
  • 2
  • 13
  • 26
3

Why not simply pass the name as constructor parameter? This doesn't hide the dependency, unlike StackFrame/StackTrace.

For example:

public class ApiController
{
    private readonly OtherClass _otherClass = new OtherClass(nameof(ApiController));

    public void GetMethod()
    {
        _otherClass.OtherMethod();
    }
}

public class OtherClass
{
    private readonly string _controllerName;

    public OtherClass(string controllerName)
    {
        _controllerName = controllerName;
    }

    public void OtherMethod()
    {
        Console.WriteLine(_controllerName);
    }
}
Caramiriel
  • 7,029
  • 3
  • 30
  • 50