Saturday 22 June 2013

Caller's Complete Name

Let's say we have a C# application where a method wants to know the full name (namespace, class name and method name) of the method that is invoking it (for saving some statistics for example). We have several ways to do this, though none of them is that elegant as I'd like.

  1. The called method takes care of this on its own, so the caller does not need to pass any additional parameter. Sounds like the ideal solution in terms of design, but it's quite of a performance killer, as we'll be using Stack traces for it.
    public void PlayRiff()
      {
       MethodBase methodBase = new StackTrace().GetFrame(1).GetMethod();  
       Console.WriteLine(methodBase.DeclaringType.FullName + "." + methodBase.Name);
      }
    
    this.Guitar.PlayRiff();
    
  2. C# 5 added the CallerMemberName attribute. It's very helpful for things like INotifyPropertyChanged. The thing is that it only provides the method (or property) name, but not the class where it resides, so it's not enough for this case. I guess a good addition for C# 6 would be something like CallerFullName
    //this would be the ideal solution... added to my C# 6 wishlist
      // public void PlayRiff4([CallerFullName] string fullCaller = null)
      // {
       // Console.WriteLine(fullCaller);
      // }
    
    //this.Guitar.PlayRiff4();
    
  3. Seems like we'll have to manually provide the information from the caller. Well, the simplest solution is just to pass string like this:
    public void PlayRiff3(string fullCaller)
      {
       Console.WriteLine(fullCaller);
      }
    
    this.Guitar.PlayRiff3("Test.Musician.Play");
    
    Such a hardcoded string is a pretty bad solution as we'll have to remember to update it each time we change the name of the namespaces, class or method, so this is like a recipe for failure.
  4. The clear winner for me is passing the type by means of this.GetType().FullName, and the method name with the aforementioned CallerMemberName.
    public void PlayRiff2(string typeStr, [CallerMemberName] string caller = null)
      {
       Console.WriteLine(typeStr + "." + caller);
      }
    
       this.Guitar.PlayRiff2(this.GetType().FullName);
       this.Guitar.PlayRiff2(typeof(Musician).FullName);
    
    Rather than GetType(), you could use typeof(MyClass). Future changes to the class name would not pose any problem, as any refactor tool would update the expression, but seems less natural to me (and I don't think there are any significant performance differences).

You can get the code here.

No comments:

Post a Comment