Reputation: 15807
I have a member that looks like this MyClass.SpecialMethod()
I need to send the name of this method as string to another method. I know that I can use TypeOf.GetMethods(..).Name
but the .. will be hardcoded and if SpecialMethod changes name there will be problems.
So what I want to do is something like this
TypeOf(MyClass).GetMethod(MyClass.SpecialMethod).Name
This is posible, and if so, how?
I know that I can get the name of the current method from inside the method but at this point its to late.
Upvotes: 1
Views: 90
Reputation: 67080
If you can use nameof()
as Jason suggested then go with it.
If you don't then write this helper method:
class MyClass {
public void SpecialMethod() {
var myName = WhatIsMyName();
}
private static string WhatIsMyName([CallerMemberName] string name= "") {
return name;
}
}
See MSDN for details. Note that if caller method is overloaded you'll get its name but it's ambiguous.
It's not supported in older C# versions (it has been introduced in .NET 4.5). If you have to deal with them then you may need to resort old trick of stack walking to check member name, roughly something like this:
[MethodImpl(MethodImplOptions.NoInlining)]
private static string WhatIsMyName() {
return new StackTrace().GetFrame(1).GetMethod().Name;
}
(note [MethodImpl(MethodImplOptions.NoInlining)]
to prevent inlining, otherwise if this call is inlined you'll skip (GetFrame(1)
instead of GetFrame(0)
) them method you want to have name of.
Be aware that all of these techniques (stack walking, caller information, expressions) actually needs to explicitly make a call to another method where nameof()
is resolved at compile-time. This may be a great difference in performance (in case it's an issue).
Upvotes: 3
Reputation: 39393
C# 6 nameof is really neat.
For now, you can use the CallerMemberName attribute in .NET 4.5, but if that is not available to you or if the name is not from the caller, you can extract the name from an Expression. Someone already made a utility for that. Following is his code. It makes the code refactoring-friendly.
How it works:
//Should return "Length", value type property
StaticReflection.GetMemberName<string>(x => x.Length);
//Should return "Data", reference type property
StaticReflection.GetMemberName<Exception>(x => x.Data);
//Should return "Clone", method returning reference type
StaticReflection.GetMemberName<string>(x => x.Clone());
//Should return "GetHashCode", method returning value type
StaticReflection.GetMemberName<string>(x => x.GetHashCode());
//Should return "Reverse", void method
StaticReflection.GetMemberName<List<string>>(x => x.Reverse());
//Should return "LastIndexOf", method with parameter
StaticReflection.GetMemberName<string>(x => x.LastIndexOf(','));
His full code:
public static class StaticReflection
{
public static string GetMemberName<T>(
this T instance,
Expression<Func<T, object>> expression)
{
return GetMemberName(expression);
}
public static string GetMemberName<T>(
Expression<Func<T, object>> expression)
{
if (expression == null)
{
throw new ArgumentException(
"The expression cannot be null.");
}
return GetMemberName(expression.Body);
}
public static string GetMemberName<T>(
this T instance,
Expression<Action<T>> expression)
{
return GetMemberName(expression);
}
public static string GetMemberName<T>(
Expression<Action<T>> expression)
{
if (expression == null)
{
throw new ArgumentException(
"The expression cannot be null.");
}
return GetMemberName(expression.Body);
}
private static string GetMemberName(
Expression expression)
{
if (expression == null)
{
throw new ArgumentException(
"The expression cannot be null.");
}
if (expression is MemberExpression)
{
// Reference type property or field
var memberExpression =
(MemberExpression) expression;
return memberExpression.Member.Name;
}
if (expression is MethodCallExpression)
{
// Reference type method
var methodCallExpression =
(MethodCallExpression) expression;
return methodCallExpression.Method.Name;
}
if (expression is UnaryExpression)
{
// Property, field of method returning value type
var unaryExpression = (UnaryExpression) expression;
return GetMemberName(unaryExpression);
}
throw new ArgumentException("Invalid expression");
}
private static string GetMemberName(
UnaryExpression unaryExpression)
{
if (unaryExpression.Operand is MethodCallExpression)
{
var methodExpression =
(MethodCallExpression) unaryExpression.Operand;
return methodExpression.Method.Name;
}
return ((MemberExpression) unaryExpression.Operand)
.Member.Name;
}
}
Upvotes: 2
Reputation: 24
Please try utilizing the CALLER INFORMATION Feature of c#
Kindly refer the URL: https://msdn.microsoft.com/en-us/library/hh534540.aspx
Upvotes: 0
Reputation: 111820
There is the Expression
way:
public static string GetName<TResult>(Expression<Func<TResult>> exp)
{
return GetName(exp != null ? exp.Body as MethodCallExpression : null);
}
public static string GetName(Expression<Action> exp)
{
return GetName(exp != null ? exp.Body as MethodCallExpression : null);
}
private static string GetName(MethodCallExpression mce)
{
if (mce == null)
{
throw new ArgumentNullException();
}
return mce.Method.Name;
}
Note that it is slow...
Use it like:
string name = GetName(() => MyClass.SpecialMethod());
or, if the method has some parameters, put some values that are of the right type. The method isn't executed, so it isn't a problem:
string name = GetName(() => MyClass.SpecialMethod(null, 5, default(DateTime)));
Upvotes: 2
Reputation: 5120
If C# 6.0 is a choice you can use the new "nameof" keyword
nameof(MyClass.SpecialMethod)
Look here for more information (section "Nameof Expressions")
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
EDIT:
Example use-cases from article:
namespace CSharp6.Tests
{
[TestClass]
public class NameofTests
{
[TestMethod]
public void Nameof_ExtractsName()
{
Assert.AreEqual<string>("NameofTests", nameof(NameofTests));
Assert.AreEqual<string>("TestMethodAttribute",
nameof(TestMethodAttribute));
Assert.AreEqual<string>("TestMethodAttribute",
nameof(
Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute));
Assert.AreEqual<string>("Nameof_ExtractsName",
string.Format("{0}", nameof(Nameof_ExtractsName)));
Assert.AreEqual<string>("Nameof_ExtractsName",
string.Format("{0}", nameof(
CSharp6.Tests.NameofTests.Nameof_ExtractsName)));
}
}
}
Upvotes: 2