Latest Replies
Saturday
Dec132008

How to Find Out Variable or Parameter Name in C#?

Sometimes it might be useful to capture the name of the variable or parameter within the code to simplify error reporting or debugging.

I keep on encountering posts and comments saying that this is not possible in C#.

Well, it is not so. And there are multiple ways to do that.

Caveat. This was written in December 2008. A lot of things have changed in .NET Microsoft since then (so this code might not work as expected). I personally no longer use need this approach due to changes is development style. However, if you need something similar, you might be interested in Microsoft Code Contracts.

Anonymous class approach

Given the C# code:

static void Main(string[] args)
{
  Console.WriteLine("Name is '{0}'", GetName(new {args}));
  Console.ReadLine();
}

The output would be:

Name is 'args'

Here's the C# code that does this:

static string GetName<T>(T item) where T : class
{
  var properties = typeof(T).GetProperties();
  Enforce.That(properties.Length == 1);
  return properties[0].Name;
}

In the method above we use anonymous type declaration to capture name of the variable (thanks to the C# compiler features) and then use reflection to get the property name.

Performance is rather reasonable - 1000000 operations complete in 0,2 seconds. Still there is a trick to make code work even faster. It is called generic type caching:

static class Cache<T>
{
  public static readonly string Name;

  static Cache()
  {
    var properties = typeof(T).GetProperties();
    Enforce.That(properties.Length == 1);
    Name = properties[0].Name;
  }
}

static string GetName<T>(T item) where T : class
{
  return Cache<T>.Name;
}

As you can see, evaluation and reflection happen only once - when the we ask for the variable name for the first time. Every other call reuses the cached value.

This technique leverages specifics of static readonly members and could be applied to other scenarios, as well.

Expression approach

As it turns out, there is another, more simple approach to find out .NET variable name, that leverages the power of Linq Expressions:

static void Main(string[] args)
{
  var domain = "matrix";
  Check(() => domain);
  Console.ReadLine();
}

static void Check<T>(Expression<Func<T>> expr)
{
  var body = ((MemberExpression)expr.Body);
  Console.WriteLine("Name is: {0}", body.Member.Name);
}

By the way, this expression-based technique is called strongly-typed reflection, and it is a really handy tool in some situations.

It is quite flexible, too. Given the expression, we can get the name and the value of the variable. So you can do this as well:

static void Check<T>(Expression<Func<T>> expr)
{
  var body = ((MemberExpression)expr.Body);
  Console.WriteLine("Name is: {0}", body.Member.Name);
  Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
    .GetValue(((ConstantExpression)body.Expression).Value));
}

The output will be:

Name is: 'domain'
Value is: 'matrix'

Note, that performance of this code (compared to the first approach) is a bit slower. With 1000000 operations it takes 3 seconds to get the name and 6 seconds to get the value. This might be or might not be an issue in your specific scenario.

I'm considering the second approach (just pulling the names) as the replacement for the name literals in the current declaration syntax of validation rules in the Shared Libraries:

Enforce.Argument(username, () => username, 
  StringIs.ValidEmail, StringIs.Limited(3,64));

Combining name and value in one expression is also really tempting, but I'd wait a bit to see if there is a way to statically cache compiled expressions.

Update: System.Reflection.Express from the Lokad Shared Libraries encapsulates methods for this kind of reflection.

IL Parsing

This approach is a bit more complex, but it does not have the performance problems of retrieving the parameter name.

We just need to replace our Check{K} method with this code snippet:

static void Check<T>(Func<T> expr)
{
  // get IL code behind the delegate
  var il = expr.Method.GetMethodBody().GetILAsByteArray();
  // bytes 2-6 represent the field handle
  var fieldHandle = BitConverter.ToInt32(il, 2);
  // resolve the handle
  var field = expr.Target.GetType()
    .Module.ResolveField(fieldHandle);

  Console.WriteLine("Name is: {0}", field.Name);
  Console.WriteLine("Value is: {0}", expr());
}

Note that instead of dealing with the Linq Expressions (and the overhead of creating them), we leverage the delegates directly. This allows to drop the overhead of retrieving the original value by 300 times, compared to the expressions, making it negligible. Performance of the name retrieval stays the same (3-4 seconds per 1000000 operations).

There are more details on this approach in the How to Get Parameter Name and Argument Value From C# Lambda via IL?

Update: System.Reflection.Reflect from the Lokad Shared Libraries has some methods for this kind of reflection.

You can subscribe to the RSS feed of this blog, if you are interested in more updates on this topic.

« How to Find C# Samples of Lokad Shared Libraries? | Main | How To Set Request Timeout in ASP.NET? »

Reader Comments (20)

Excellent, and very creative.

Now, can you extend it so it can be used inside a different method?

Say you have a method EnsureNotNull() as part of maintaining your design contract (probably similar to your Enforce class, but I'm not certain). Now, at the minute you'd call it with something like:

DesignContract.EnsureNotNull (parameter1, "parameter1");

so that the parameter name could be specified in the exception. Now, I'd really rather remove that string parameter and keep the method call really simple (even if I have to really complicate the method itself) so that I'd just call:

DesignContract.EnsureNotNull (parameter1);

Your code is nearly there, but since it would run in the EnsureNotNull method, it gets the name of the parameter to that method, rather than the name of the variable one stack frame up.

I know I could just use something like:

DesignContract.EnsureNotNull (parameter1, ContractParameter.GetName (new {parameter1}));

but that looks like a lot of complicated duplication in every call.

So, any creative ideas on how to get this to work?

Many thanks,

Geoff

December 15, 2008 | Unregistered CommenterGeoff Taylor

I'm too lazy to test but would this code hit the cache for the second writeline?

Console.WriteLine("Name is '{0}'", GetName(new {args}));
Console.WriteLine("Name is '{0}'", GetName(new {args}));

Is the compiler smart enough to use the same anonymous type for both calls?

December 15, 2008 | Unregistered CommenterBill Pierce

Bill,

yes, it does. That is required by Linq.

December 16, 2008 | Registered CommenterRinat Abdullin

Geoff,

my closest bet is DesignContract.EnsureNotNull (() =>parameter1);

Check out the article update.

December 16, 2008 | Registered CommenterRinat Abdullin

Hello, Rinat.
Really interesting series of posts.
Thanks.

December 19, 2008 | Unregistered CommenterNik Govorov

do you have any solution for C# 2.0 ?

December 23, 2008 | Unregistered CommenterSiraj

i am using vs 2005 right not , , your solution is valid only for C# 3.0

December 23, 2008 | Unregistered CommenterSiraj

Siraj,

The solution is valid for C# 3.0, indeed. But you can still target .NET 2.0 with it. Check out this post for details.

December 23, 2008 | Registered CommenterRinat Abdullin

ya , but you still need VS2008 , do you have any thing for VS 2005 with .net 2.0 ?

December 23, 2008 | Unregistered CommenterSiraj

just tell me alternative to " new {args} " in C# 2.0 ;

December 23, 2008 | Unregistered CommenterSiraj

Siraj, there is not any reasonable alternative in VS 2005.

December 24, 2008 | Registered CommenterRinat Abdullin

thanks any way i am really greatfull !

December 24, 2008 | Unregistered CommenterSiraj

Why do you use

var properties = typeof(T).GetProperties();
Enforce.That(properties.Length == 1);
Name = properties[0].Name;

instead of

Name = typeof(T).Name;
?

January 16, 2009 | Unregistered CommenterSvish

And why

((FieldInfo)body.Member)
.GetValue(((ConstantExpression)body.Expression).Value)

instead of

expr.Compile()()

?

Im sure you have some reason, I'm just not very experienced with this stuff, and is pretty curious... want to learn :p

January 16, 2009 | Unregistered CommenterSvish

1. Because "f__AnonymousType0`1" is not the name we are looking for.
2. Performance is much better.

January 16, 2009 | Registered CommenterRinat Abdullin

People deserve very good life and personal loans or just collateral loan would make it better. Just because people's freedom is grounded on money.

March 27, 2010 | Unregistered CommenterCALDWELLRosa22


static string GetParameterName(int parameterIndex)
{
    var frame = new StackFrame(1);
    var method = frame.GetMethod();
    var parameter = method.GetParameters()[parameterIndex];
    return parameter.Name;
}

This works only for "current" method, where you call GetParameterName method. For deeper calls you should change StackFrame constructor's parameter.

July 7, 2010 | Unregistered CommenterRadek

Just tested the performance and as of 22.6.2011 (VS 2010, .NET 4.0), expression approach (((MemberExpression)expression.Body).Member.Name) is about two times faster than IL parsing for 10 000 000 iterations.

June 22, 2011 | Unregistered CommenterJiri Sofka

Hi, the first approach does not work in every case to me (. NET 4.0 and VS2010) and others never return to the original name of the variable. I mean:

myClass
{
string Name = "Alberto";

...

void foo(object obj)
{
MessageBox.show("Field: \"" + GetName(obj) + "\" - Value: \"" + obj.ToString() + "\"");
}

Always print

Field: "obj" - Value: "Alberto"

instead of

Field: "Name" - Value: "Alberto"

What am I doing wrong?

January 4, 2012 | Unregistered CommenterAlberto

Alberto, this post was written in December 2008. A lot of things have changed in .NET Microsoft since then (so this code might not work as expected). I personally no longer use need this approach due to changes is development style. However, if you need something similar, you might be interested in Microsoft Code Contracts.

Best regards,
Rinat

January 5, 2012 | Registered CommenterRinat Abdullin

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>