简体   繁体   中英

Retrieving the original variable/argument name, as it was named, in the caller method – (similar to [CallerMemberName] attribute)

Is there any way to get the original name of the variable/argument , as it was named, in the caller method ? In a way, it's like [CallerMemberName] attribute, but for the variable/argument.

UPDATE 2018.02: This question is similar to these ones: 1 , 2 and 3 , but there is still no valid, standardized solution at the begin of 2018. The proposed solution: using Expression<Func<T>> can have non-documented effects and is quite CPU-intensive , since converting a code into an expression to get the original name of a variable only is not a simple action. It is like creating a 1М-sized empty array to store single integer number instead of simple integer – very not effective («з гармати по горобцям»).

Short example:

int ComplexMathMethod(int startPos, int endPos,
    int backgroundStartPos, int backgroundEndStart, 
    int squareStartPos, // …and other positions… )
{
    CheckPosition(startPos);
    CheckPosition(endPos);
    CheckPosition(backgroundStartPos);
    CheckPosition(backgroundEndStart);
    CheckPosition(squareStartPos);
    // …and so on…  
}

void CheckPosition(int position)
{
    bool isValidPosition = // complex check;
    if (!isValidPosition)
    {
        throw new ArgumentException(nameof(position));

        // --->>> It will be extremely convenient to automatically replace "nameof(position)" with original 
        // name of variable/argument from caller method, like: "startPos", "endPos", "backgroundStartPos", etc.

        // Additional string argument with original variable name is not a good solution too.
        // Because, I have similar "check" method that checks 4 connected position’s integers at once.
        // Therefore, 4 input ints with additional +4 string arguments will make a mess.
    }
}

Long example:

void ShowMessage(string messageStr)
{

    // Problem: get original variable/argument name, as it was called, in the caller method.
    string callerMethodArgName;

    // If method was called from "Main" method, than callerMethodArgName should equal to "finishMsg".
    // If method was called from "DoWork" method, than callerMethodArgName should equal to "taskName".
    // If method was called from "StartProgram" method, than callerMethodArgName should equal to "startingMsg".    

    // …missing logic, reflection code, etc.

    // The next line is just a stub.
    callerMethodArgName = nameof(messageStr);

    Console.WriteLine($"Variable name is \"{callerMethodArgName}\"; value is \"{messageStr}\".");

}

void Main()
{
    StartProgram();
    DoWork("Programming…");

    // Example 1.
    string finishMsg = "Finish!";
    ShowMessage(finishMsg);
}

// Example, caller method 2.
void StartProgram()
{
    string startingMsg = "Starting program";
    ShowMessage(startingMsg);
}

// Example, caller method 3.
void DoWork(string taskName)
{
    ShowMessage(taskName);
}

Current program output:

// Variable name is "messageStr"; value is "Starting program".
// Variable name is "messageStr"; value is "Programming…".
// Variable name is "messageStr"; value is "Finish!".

Required/desired output:

// Variable name is "startingMsg"; value is "Starting program".
// Variable name is "taskName"; value is "Programming…".
// Variable name is "finishMsg"; value is "Finish!".

I am not sure if this is possible at all. I spent several hours playing with reflection, but unfortunately, it was fruitless. This would be extremely convenient while debugging in real project.

You can use Linq.Expressions for this. First you need to change a signature of CheckPosition like this:

void CheckPosition(Expression<Func<int>> positionExpression)

After that you can call it like this:

CheckPosition(() => startPos);

Full code:

int ComplexMathMethod(int startPos, int endPos,
    int backgroundStartPos, int backgroundEndStart,
    int squareStartPos) // …and other positions… )
{
    CheckPosition(() => startPos);
    CheckPosition(() => endPos);
    CheckPosition(() => backgroundStartPos);
    CheckPosition(() => backgroundEndStart);
    CheckPosition(() => squareStartPos);
    // …and so on…  

    return 0;
}

void CheckPosition(Expression<Func<int>> positionExpression)
{
    var name = ((MemberExpression) positionExpression.Body).Member.Name;
    var value = positionExpression.Compile().Invoke();

    Console.WriteLine($"Name = {name}, value ={value}");
}

You can do this with the CallerArgumentExpression attribute from C# 10 and up.

The CallerArgumentExpression attribute allows accessing the name of an argument that was passed as a parameter to a method.

Here's an example based on your original code:

void ShowMessage(
    string messageStr, 
    [CallerArgumentExpression("messageStr")] string callerMethodArgName= "")
{
    // callerMethodArgName contains the name of the argument that was passed in to the messageStr parameter
    Console.WriteLine($"Variable name is \"{callerMethodArgName}\"; value is \"{messageStr}\".");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM