Search code examples
c#this

Generically get the name of the current class?


When logging data, I want a generic reference to the containing class. That way, if the code is moved elsewhere, the class name will change accordingly. (Otherwise, if the code moves to nameof(Class2), it will still be logged incorrectly as nameof(Class1)). For example:

class Class_Name {
   ICommand Command_Name =>
        new RelayCommand(() =>
        {
           // An loggable event occurs

           // Is there a smart and uncomplicated way of doing this generically?
           var provenance = $"{nameof(Class_Name)}.{nameof(Command_Name)}";

           // The event of whatever kind gets logged
      });
   }

   // OR
   void Method_Name() {
      var provenance = $"{nameof(Class_Name)}.{nameof(Method_Name)}";
   }
}

Using a generic nameof(this), where this should refer to the class itself, causes a compilation error: CS8081: Expression does not have a name. Using this.GetType() causes the same problem.

Not really understanding why the this keyword does not refer to the containing class in this context. Is there a way to refer to the current class generically?


Solution

  • If you combine the suggestion in the comments (this.GetType().Name) with a [CallerMemberName] attribute via a helper method, you can accomplish what you're looking for in a reusable fashion.

    public class Class_Name
    {
        public void Method_Name()
        {
            var provenance = CreateProvenance();
            Console.WriteLine(provenance);
        }
    
        private string CreateProvenance([CallerMemberName] string methodName = "")
        {
            return $"{this.GetType().Name}.{methodName}";
        }
    }
    

    This outputs "Class_Name.Method_Name".

    You can even turn this into a handy extension method that allows you to call it from any method.

    public class Class_Name
    {
        public void Method_Name()
        {
            var provenance = this.CreateProvenance();
            Console.WriteLine(provenance);
        }
    }
    
    public static class ProvenanceExtensions
    {
        public static string CreateProvenance(this object context, 
            [CallerMemberName] string methodName = "")
        {
            return $"{context.GetType().Name}.{methodName}";
        }
    }
    

    As Jeppe Stig Nielsen pointed out, you may not want the inheriting runtime type to be used, which is what context.GetType().Name will return. If you want to get the compile-time type instead, you can use generics.

    public static class ProvenanceExtensions
    {
        public static string CreateProvenance<T>(this T context, 
            [CallerMemberName] string methodName = "")
        {
            return $"{typeof(T).Name}.{methodName}";
        }
    }