查找传递给函数的变量名称

Gat*_*ler 60 c#

让我用下面的例子来解释我的问题:

public string ExampleFunction(string Variable) {
    return something;
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(WhatIsMyName);
Run Code Online (Sandbox Code Playgroud)

当我将变量"WhatIsMyName"传递给示例函数时,我希望能够获取原始变量名称的字符串.也许是这样的:

Variable.OriginalName.ToString()
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点?

小智 61

我知道这篇文章确实很旧,但由于 C#10 编译器现在有一种方法,我想我应该分享一下,以便其他人知道。

您现在可以使用CallerArgumentExpressionAttribute,如图所示

// Will throw argument exception if string IsNullOrEmpty returns true
public static void ValidateNotNullorEmpty(
  this string str,
  [CallerArgumentExpression("str")]string strName = null
)
{       
  if (string.IsNullOrEmpty(str))
  {
    throw new ArgumentException($"'{strName}' cannot be null or empty.", strName);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在致电:

param.ValidateNotNullorEmpty();
Run Code Online (Sandbox Code Playgroud)

会抛出错误:"param cannot be null or empty."

而不是“str 不能为空”


Kon*_*lph 58

您想要的不是直接的,但您可以在C#3.0中使用表达式:

public void ExampleFunction(Expression<Func<string, string>> f) {
    Console.WriteLine((f.Body as MemberExpression).Member.Name);
}

ExampleFunction(x => WhatIsMyName);
Run Code Online (Sandbox Code Playgroud)

请注意,这取决于未指定的行为,虽然它在Microsoft的当前C#和VB编译器以及 Mono的C#编译器中都有效,但无法保证在将来的版本中不会停止工作.

  • @Douglas谢谢,很高兴知道.对于它的价值,Eric Lippert对此的咆哮似乎有点不成熟.其他语言*做*标准化,他的整个咆哮似乎是基于这样一个事实,即指定这种行为是baaaad,这似乎是非常错误的,或者至少是完全不明显的.相反:这是该功能的*自然*实现,它高效,安全,并且没有先验的理由不对其进行标准化. (3认同)
  • 出于"表达式"的目的,一个局部变量实际上*是*属性(`.Member.Name` - 这是编译器为实现lambda表达式而创建的闭包的直接结果)所以上面的代码也应该为物业工作. (2认同)

joh*_*y 5 31

我知道这是一个老问题,但在C#6.0中他们引入了应该解决这个问题的运营商名称.运算符的名称解析传递给它的变量的名称.

您案例的用法如下所示:

public string ExampleFunction(string variableName) {
      //Construct your log statement using c# 6.0 string interpolation
       return $"Error occurred in {variableName}";
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(nameof(WhatIsMyName));
Run Code Online (Sandbox Code Playgroud)

一个主要好处是它在编译时完成,

表达式的名称是常量.在所有情况下,在编译时评估nameof(...)以生成字符串.它的参数不在运行时进行评估,并且被认为是无法访问的代码(但它不会发出"无法访问的代码"警告).

更多信息可以在这里找到

旧版本的C 3.0及以上版本
以Nawfals为基础回答

GetParameterName2(new { variable });

//Hack to assure compiler warning is generated specifying this method calling conventions
[Obsolete("Note you must use a single parametered AnonymousType When Calling this method")]
public static string GetParameterName<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}
Run Code Online (Sandbox Code Playgroud)

  • 对不起,但这个答案是对的.你的`nameof(s)`的例子只返回字符串`s`而不是调用f函数的参数变量的名称.在原始问题中,它会将`Variable`传递给`ArgumentNullException`而不是`WhatsMyName`. (27认同)
  • 对于转到c#6的人来说,这是正确的答案. (2认同)
  • @ johnny5现在更清楚了.但我认为它仍然不是@Gatekiller想要的.我想他想在`ExampleFunction`中访问`WhatsMyName`和`nameof(WhatsMyName)`的内容.据我所知,除了为`ExampleFunction`提供两个参数之外别无他法.如果你只需要传递`WhatsMyName`作为唯一参数并在`ExampleFunction`中以某种方式得到`nameof(WhatsMyName)`那就太棒了. (2认同)

Rin*_*lin 19

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

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

更多详细信息,请参阅此博客文章.

  • 链接坏了,虽然解决方案看起来很整洁.也许你可以在这里添加一些细节? (3认同)

naw*_*fal 14

三种方式:

1)没有反思的东西:

GetParameterName1(new { variable });

public static string GetParameterName1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return item.ToString().TrimStart('{').TrimEnd('}').Split('=')[0].Trim();
}
Run Code Online (Sandbox Code Playgroud)

2)使用反射,但这比其他两个更快.

GetParameterName2(new { variable });

public static string GetParameterName2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}
Run Code Online (Sandbox Code Playgroud)

3)最慢的,不要使用.

GetParameterName3(() => variable);

public static string GetParameterName3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    return ((MemberExpression)expr.Body).Member.Name;
}
Run Code Online (Sandbox Code Playgroud)

要获取组合参数名称和值,可以扩展这些方法.当然,如果你将参数作为另一个参数单独传递,那么它很容易获得值,但这是不优雅的.代替:

1)

public static string GetParameterInfo1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = item.ToString().TrimStart('{').TrimEnd('}').Split('=');
    return "Parameter: '" + param[0].Trim() +
           "' = " + param[1].Trim();
}
Run Code Online (Sandbox Code Playgroud)

2)

public static string GetParameterInfo2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = typeof(T).GetProperties()[0];
    return "Parameter: '" + param.Name +
           "' = " + param.GetValue(item, null);
}
Run Code Online (Sandbox Code Playgroud)

3)

public static string GetParameterInfo3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    var param = (MemberExpression)expr.Body;
    return "Parameter: '" + param.Member.Name +
           "' = " + ((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value);
}
Run Code Online (Sandbox Code Playgroud)

1和2现在具有相当的速度,3再次缓慢.


fib*_*iel 8

继续Caller*属性系列(即CallerMemberNameCallerFilePathCallerLineNumber),CallerArgumentExpressionAttribute自 C# Next 起可用(更多信息请参见此处)。

以下示例的灵感来自 Paul Mcilreavy 的C# 8.0 中的 CallerArgumentExpression 属性

public static void ThrowIfNullOrWhitespace(this string self, 
             [CallerArgumentExpression("self")] string paramName = default)
{
    if (self is null)
    {
        throw new ArgumentNullException(paramName);
    }

    if (string.IsNullOrWhiteSpace(self))
    {
        throw new ArgumentOutOfRangeException(paramName, self, "Value cannot be whitespace");
    }        
}
Run Code Online (Sandbox Code Playgroud)


blo*_*oop 5

是! 有可能的。很长时间以来,我一直在寻找解决方案,最后终于提出了解决该问题的办法(有点讨厌)。我不建议将此作为程序的一部分,我只认为它可以在调试模式下工作。对我而言,这无关紧要,因为我只在控制台类中将其用作调试工具,所以可以这样做:

int testVar = 1;
bool testBoolVar = True;
myConsole.Writeline(testVar);
myConsole.Writeline(testBoolVar);
Run Code Online (Sandbox Code Playgroud)

到控制台的输出将是:

testVar: 1
testBoolVar: True
Run Code Online (Sandbox Code Playgroud)

这是我用来执行此操作的函数(不包括控制台类的包装代码。

    public Dictionary<string, string> nameOfAlreadyAcessed = new Dictionary<string, string>();
    public string nameOf(object obj, int level = 1)
    {
        StackFrame stackFrame = new StackTrace(true).GetFrame(level);
        string fileName = stackFrame.GetFileName();
        int lineNumber = stackFrame.GetFileLineNumber();
        string uniqueId = fileName + lineNumber;
        if (nameOfAlreadyAcessed.ContainsKey(uniqueId))
            return nameOfAlreadyAcessed[uniqueId];
        else
        {
            System.IO.StreamReader file = new System.IO.StreamReader(fileName);
            for (int i = 0; i < lineNumber - 1; i++)
                file.ReadLine();
            string varName = file.ReadLine().Split(new char[] { '(', ')' })[1];
            nameOfAlreadyAcessed.Add(uniqueId, varName);
            return varName;
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 真是可怕的,出色的工作!如果“nameof”允许您查找堆栈的一级,那就太好了,因此“nameof(x,1)”将为您提供调用者范围内表达式的名称,该表达式作为参数“x”传递给当前方法。 (2认同)

小智 5

为了创建良好的异常消息,使人们能够更好地查明错误,这将非常有用。行号有帮助,但您可能无法在产品中获得它们,当您获得它们时,如果代码中有大语句,您通常只会获得整个语句的第一行。

例如,如果您在未设置的可为 null 的情况下调用 .Value,您将收到带有失败消息的异常,但由于缺少此功能,您将看不到哪个属性为 null。如果您在一条语句中执行此操作两次,例如为某个方法设置参数,您将无法看到未设置的可空值。

创建像Verify.NotNull(myvar, nameof(myvar)) 这样的代码是迄今为止我发现的最好的解决方法,但是如果不需要添加额外的参数,那就太好了。


Gis*_*shu -11

不,我不这么认为。

您使用的变量名称是为了方便和可读。如果我没记错的话,编译器不需要它,只是将其丢弃。

如果有帮助,您可以定义一个带有NamedParameter属性Name和 的新类Param。然后将此对象作为参数传递。

  • 这个原始问题的较新副本可能有一个可能的答案是:http://stackoverflow.com/questions/9801624/get-name-of-a-variable-or-parameter (13认同)
  • @alfa,您链接到的答案给出了参数名称,而不是按照本问题要求传递给函数的参数名称。 (4认同)
  • 实际参数是计算表达式,而不是变量。 (3认同)