2

I have multiple instances of a class that I would like to track within an array that holds this type of class that I do not control. To do so I have created a List<MyClass> that I then compare to each value in the MyClass[] that I do not control.

I then print out the name of the class references with the C#6.0 operator nameOf. To my surprise (well its obvious now why) when I go to print the name of the class references when checking it from the List the loop variable name is printed instead of the class reference it references.

Is there a way to print the name of a variable that is referenced by another variable?

(Also what is the proper jargon to use for this question, I feel like I butchered it?)


Here is an example of the issue using Control

Control x = new Control();
Control y = new Control();
Control z = new Control();
var controlArr = new Control[] { x, y, z };
var controlList = new List<Control>() { x, y, z };
for (int i = 0; i < controlArr.Length; i++)
{
    Control anArrControl = controlArr[i];
    foreach (var aListControl in controlList)
        if (anArrControl == aListControl)
            Console.Out.WriteLine("Control[" + i + "]: " + nameof(aListControl));
}

This code will print

Control[0]: aListControl 
Control[1]: aListControl 
Control[2]: aListControl 

Instead of

Control[0]: x
Control[1]: y
Control[2]: z

EDIT: This example is simple in that if the Control[] does not contain a reference in the List<Control> nothing will be printed. In my actual application I take care of this. I simply put the example code to demonstrate what I mean when I asked the question, I didn't feel as if I worded it properly.

KDecker
  • 6,928
  • 8
  • 40
  • 81
  • 2
    No. Think about what would happen if instead of `{ x, y, z }`, you initialized the collection with `{ x, y, new Control() }`. What would you expect it to print? – itsme86 Oct 19 '16 at 20:21
  • no, that's not possible. At that point `x` and `Control[i]` are just pointing to the same object. – Jonesopolis Oct 19 '16 at 20:23
  • 2
    It does not work this way. `nameof` is evaluated at compile time, in fact by "expressing its argument as a literal": see [here](http://stackoverflow.com/questions/26573102/is-nameof-evaluated-at-compile-time?rq=1) – Cee McSharpface Oct 19 '16 at 20:24
  • I agree with the compile time comment, that's probably the most important thing to realize here – GEEF Oct 19 '16 at 20:26
  • Name does not exist. Only address. Even when you use nameof that string is ready in compile time not after you run your program – M.kazem Akhgary Oct 19 '16 at 20:27
  • Seems like you're looking for a `Dictionary`. – CodeCaster Oct 19 '16 at 20:30
  • idea: take the `nameof`s of the original array members as you declare them, and take them along in a `Tuple`. Ugly but might work. – Cee McSharpface Oct 19 '16 at 20:32
  • You are using nameof(aListControl)), thats why its giving correct value. If you do nameof(aListControl[i]) then it will give you x,y,z – Vivek Nuna Oct 19 '16 at 20:32
  • @viveknuna This will not work, you will get an error "Expression does not have a name". – KDecker Oct 19 '16 at 20:35

2 Answers2

3

On MSDN you find a reference of use cases that motivated the creation of the nameof function. The name of a reference is not defined at compile time. Given

 var a = new object();
 var b = a;
 var array = new [] {a, b}

What should nameof(array[1]) return ? It is the same referenced object.

Therefore you have to think of another workaround to achieve your goal. Perhaps a Dictionary might help you. Like

Control x = new Control();
Control y = new Control();
Control z = new Control();
var references = new Dictionary<Control, string> {
    { x, "x" }, { y, "y" }, {z, "z"}
};
var controlArr = new Control[] { x, y, z };
var controlList = new List<Control>() { x, y, z };
for (int i = 0; i < controlArr.Length; i++)
{
    Control anArrControl = controlArr[i];
    foreach (var aListControl in controlList)
        if (anArrControl == aListControl)
            Console.Out.WriteLine("Control[" + i + "]: " + references[aListControl]);
}
Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
  • 1
    This does seem to be basically the only option (You could also just use `nameOf(x)` instead of the literal string too!). Thanks rboe! – KDecker Oct 19 '16 at 20:37
2

Nope, that won't work, since controlList does not contain x, y and z - it contains the controls that x, y and z refered to at the time of assignment.

There are two alternatives that you might want to consider:

  1. Store the control and its variable name in a List<Tuple<Control, String>>:

    var controlList = new[] {
        Tuple.Create(x, nameof(x)),
        Tuple.Create(y, nameof(y)),
        Tuple.Create(z, nameof(z))
    }.ToList();
    

    Obviously, your comparison then needs to be changed to anArrControl == aListControl.Item1.

  2. Some classes (such as WinForm's Control) have a Name property. Maybe that name suits your purpose as well?

Heinzi
  • 167,459
  • 57
  • 363
  • 519