How to Get a Variable's Name as It Was Physically Typed in Its Declaration

Finding the declared name of a variable

Reflection can get you the names of the method arguments, also local variables in the context of the method body, but that's where the buck stops. The only way to get them is by reading the debug information file, the .pdb that's generated for your program.

There's a good reason for that. One of the most important jobs performed by the jitter is to remove local variables from the generated machine code. And substitute them with cpu registers. It is a very important optimization that can make a great deal of difference in the speed of the code. Memory is slow, cpu registers are fast. That's an optimization that's performed in the release build of your program. Also very visible when you try to debug a release version of your program by attaching a debugger. You'll get a lot of warnings from the debugger, telling you that it doesn't know what you mean when you ask it to inspect a local variable. And also the downfall when using the .pdb file.

A backgrounder answer on the kind of optimizations performed by the jitter is available here.

How to get variable name using reflection?

It is not possible to do this with reflection, because variables won't have a name once compiled to IL. However, you can use expression trees and promote the variable to a closure:

static string GetVariableName<T>(Expression<Func<T>> expr)
{
var body = (MemberExpression)expr.Body;

return body.Member.Name;
}

You can use this method as follows:

static void Main()
{
var someVar = 3;

Console.Write(GetVariableName(() => someVar));
}

Note that this is pretty slow, so don't use it in performance critical paths of your application. Every time this code runs, several objects are created (which causes GC pressure) and under the cover many non-inlinable methods are called and some heavy reflection is used.

For a more complete example, see here.

UPDATE

With C# 6.0, the nameof keyword is added to the language, which allows us to do the following:

static void Main()
{
var someVar = 3;

Console.Write(nameof(someVar));
}

This is obviously much more convenient and has the same cost has defining the string as constant string literal.

How do you get a C# property name as a string with reflection?

You can use Expressions to achieve this quite easily. See this blog for a sample.

This makes it so you can create an expression via a lambda, and pull out the name. For example, implementing INotifyPropertyChanged can be reworked to do something like:

public int MyProperty {
get { return myProperty; }
set
{
myProperty = value;
RaisePropertyChanged( () => MyProperty );
}
}

In order to map your equivalent, using the referenced "Reflect" class, you'd do something like:

string propertyName = Reflect.GetProperty(() => SomeProperty).Name;

Viola - property names without magic strings.

How to retrieve a variable's name in python at runtime?

here a basic (maybe weird) function that shows the name of its argument...
the idea is to analyze code and search for the calls to the function (added in the init method it could help to find the instance name, although with a more complex code analysis)

def display(var):
import inspect, re
callingframe = inspect.currentframe().f_back
cntext = "".join(inspect.getframeinfo(callingframe, 5)[3]) #gets 5 lines
m = re.search("display\s+\(\s+(\w+)\s+\)", cntext, re.MULTILINE)
print m.group(1), type(var), var

please note:
getting multiple lines from the calling code helps in case the call was split as in the below example:

display(
my_var
)

but will produce unexpected result on this:

display(first_var)
display(second_var)

If you don't have control on the format of your project you can still improve the code to detect and manage different situations...

Overall I guess a static code analysis could produce a more reliable result, but I'm too lazy to check it now

How can I get the data type of a variable in C#?

Other answers offer good help with this question, but there is an important and subtle issue that none of them addresses directly. There are two ways of considering type in C#: static type and run-time type.

Static type is the type of a variable in your source code. It is therefore a compile-time concept. This is the type that you see in a tooltip when you hover over a variable or property in your development environment.

Run-time type is the type of an object in memory. It is therefore a run-time concept. This is the type returned by the GetType() method.

An object's run-time type is frequently different from the static type of the variable, property, or method that holds or returns it. For example, you can have code like this:

object o = "Some string";

The static type of the variable is object, but at run time, the type of the variable's referent is string. Therefore, the next line will print "System.String" to the console:

Console.WriteLine(o.GetType()); // prints System.String

But, if you hover over the variable o in your development environment, you'll see the type System.Object (or the equivalent object keyword).

For value-type variables, such as int, double, System.Guid, you know that the run-time type will always be the same as the static type, because value types cannot serve as the base class for another type; the value type is guaranteed to be the most-derived type in its inheritance chain. This is also true for sealed reference types: if the static type is a sealed reference type, the run-time value must either be an instance of that type or null.

Conversely, if the static type of the variable is an abstract type, then it is guaranteed that the static type and the runtime type will be different.

To illustrate that in code:

// int is a value type
int i = 0;
// Prints True for any value of i
Console.WriteLine(i.GetType() == typeof(int));

// string is a sealed reference type
string s = "Foo";
// Prints True for any value of s
Console.WriteLine(s == null || s.GetType() == typeof(string));

// object is an unsealed reference type
object o = new FileInfo("C:\\f.txt");
// Prints False, but could be true for some values of o
Console.WriteLine(o == null || o.GetType() == typeof(object));

// FileSystemInfo is an abstract type
FileSystemInfo fsi = new DirectoryInfo("C:\\");
// Prints False for all non-null values of fsi
Console.WriteLine(fsi == null || fsi.GetType() == typeof(FileSystemInfo));

Another user edited this answer to incorporate a function that appears below in the comments, a generic helper method to use type inference to get a reference to a variable's static type at run time, thanks to typeof:

Type GetStaticType<T>(T x) => typeof(T);

You can use this function in the example above:

Console.WriteLine(GetStaticType(o)); // prints System.Object

But this function is of limited utility unless you want to protect yourself against refactoring. When you are writing the call to GetStaticType, you already know that o's static type is object. You might as well write

Console.WriteLine(typeof(object)); // also prints System.Object!

This reminds me of some code I encountered when I started my current job, something like

SomeMethod("".GetType().Name);

instead of

SomeMethod("String");

How are variable names stored in memory in C?

Variable names don't exist anymore after the compiler runs (barring special cases like exported globals in shared libraries or debug symbols). The entire act of compilation is intended to take those symbolic names and algorithms represented by your source code and turn them into native machine instructions. So yes, if you have a global variable_name, and compiler and linker decide to put it at 0xHow to Get a Variable's Name as It Was Physically Typed in Its Declaration, then wherever it is used in the code, it will just be accessed via that address.

So to answer your literal questions:

How does the compiler recognize that the string "variable_name" is associated with that particular memory address?

The toolchain (compiler & linker) work together to assign a memory location for the variable. It's the compiler's job to keep track of all the references, and linker puts in the right addresses later.

Is the string "variable_name" stored somewhere in memory?

Only while the compiler is running.

Does the compiler just substitute variable_name for 0xHow to Get a Variable's Name as It Was Physically Typed in Its Declaration whenever it sees it, and if so, wouldn't it have to use memory in order to make that substitution?

Yes, that's pretty much what happens, except it's a two-stage job with the linker. And yes, it uses memory, but it's the compiler's memory, not anything at runtime for your program.

An example might help you understand. Let's try out this program:

int x = 12;

int main(void)
{
return x;
}

Pretty straightforward, right? OK. Let's take this program, and compile it and look at the disassembly:

$ cc -Wall -Werror -Wextra -O3    example.c   -o example
$ otool -tV example
example:
(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp,%rbp
0000000100000f64 movl 0x00000096(%rip),%eax
0000000100000f6a popq %rbp
0000000100000f6b ret

See that movl line? It's grabbing the global variable (in an instruction-pointer relative way, in this case). No more mention of x.

Now let's make it a bit more complicated and add a local variable:

int x = 12;

int main(void)
{
volatile int y = 4;
return x + y;
}

The disassembly for this program is:

(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp,%rbp
0000000100000f64 movl $0x00000004,0xfc(%rbp)
0000000100000f6b movl 0x0000008f(%rip),%eax
0000000100000f71 addl 0xfc(%rbp),%eax
0000000100000f74 popq %rbp
0000000100000f75 ret

Now there are two movl instructions and an addl instruction. You can see that the first movl is initializing y, which it's decided will be on the stack (base pointer - 4). Then the next movl gets the global x into a register eax, and the addl adds y to that value. But as you can see, the literal x and y strings don't exist anymore. They were conveniences for you, the programmer, but the computer certainly doesn't care about them at execution time.



Related Topics



Leave a reply



Submit