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

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

public string ExampleFunction(string Variable) { return something; } string WhatIsMyName = "Hello World"'; string Hello = ExampleFunction(WhatIsMyName); 

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

 Variable.OriginalName.ToString() 

有没有办法做到这一点?

** No。**我不这么认为。

您使用的变量名称是为了您的方便和可读性。 如果我没有弄错,编译器就不需要它了。

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

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

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

请注意,这依赖于未指定的行为,虽然它在Microsoft的当前C#和VB编译器以及 Mono的C#编译器中都能正常工作,但不能保证在未来的版本中不会停止工作。

我知道这是一个老问题,但在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)); 

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

表达式的名字是一个常量。 在所有情况下,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; } 
 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; } 

更多细节在这篇博客文章 。

三种方式:

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(); } 

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; } 

3)最慢的,不要用。

 GetParameterName3(() => variable); public static string GetParameterName3<T>(Expression<Func<T>> expr) { if (expr == null) return string.Empty; return ((MemberExpression)expr.Body).Member.Name; } 

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

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(); } 

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); } 

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); } 

1和2现在速度相当,3又缓慢。

不,但是每当你发现自己在做这样非常复杂的事情时,你可能想重新考虑你的解决方案。 请记住,代码应该比写它更容易阅读。

System.Environment.StackTrace会给你一个包含当前调用栈的字符串。 你可以解析得到的信息,其中包括每个调用的变量名称。

是! 有可能的。 我一直在寻找这个解决方案很长一段时间,终于拿出一个解决它的黑客(这有点讨厌)。 我不建议使用这个作为你的程序的一部分,我只认为它在调试模式下工作。 对我来说,这并不重要,因为我只用它作为我的控制台类中的调试工具,所以我可以这样做:

 int testVar = 1; bool testBoolVar = True; myConsole.Writeline(testVar); myConsole.Writeline(testBoolVar); 

输出到控制台将是:

 testVar: 1 testBoolVar: True 

这是我用来做的功能(不包括我的控制台类的包装代码。

  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; } } 

那么试试这个工具类,

 public static class Utility { public static Tuple<string, TSource> GetNameAndValue<TSource>(Expression<Func<TSource>> sourceExpression) { Tuple<String, TSource> result = null; Type type = typeof (TSource); Func<MemberExpression, Tuple<String, TSource>> process = delegate(MemberExpression memberExpression) { ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression; var name = memberExpression.Member.Name; var value = ((FieldInfo)memberExpression.Member).GetValue(constantExpression.Value); return new Tuple<string, TSource>(name, (TSource) value); }; Expression exception = sourceExpression.Body; if (exception is MemberExpression) { result = process((MemberExpression)sourceExpression.Body); } else if (exception is UnaryExpression) { UnaryExpression unaryExpression = (UnaryExpression)sourceExpression.Body; result = process((MemberExpression)unaryExpression.Operand); } else { throw new Exception("Expression type unknown."); } return result; } } 

和用户喜欢

  /*ToDo : Test Result*/ static void Main(string[] args) { /*Test : primivit types*/ long maxNumber = 123123; Tuple<string, long> longVariable = Utility.GetNameAndValue(() => maxNumber); string longVariableName = longVariable.Item1; long longVariableValue = longVariable.Item2; /*Test : user define types*/ Person aPerson = new Person() { Id = "123", Name = "Roy" }; Tuple<string, Person> personVariable = Utility.GetNameAndValue(() => aPerson); string personVariableName = personVariable.Item1; Person personVariableValue = personVariable.Item2; /*Test : anonymous types*/ var ann = new { Id = "123", Name = "Roy" }; var annVariable = Utility.GetNameAndValue(() => ann); string annVariableName = annVariable.Item1; var annVariableValue = annVariable.Item2; /*Test : Enum tyoes*/ Active isActive = Active.Yes; Tuple<string, Active> isActiveVariable = Utility.GetNameAndValue(() => isActive); string isActiveVariableName = isActiveVariable.Item1; Active isActiveVariableValue = isActiveVariable.Item2; } 

不。你的字符串变量的引用被传递给函数 – 没有任何内在的metadeta包含它。 即使是反思也不会让你摆脱困境 – 从一个单一的参考类型向后工作并没有让你足够的信息去做你需要做的事情。

最好回到这个画板上!

RP

您可以使用反射来获取对象的所有属性,而不是通过循环来获取属性的名称(属性的名称)与传入的参数相匹配的值。

感谢所有的答复。 我想我只需要去做我现在正在做的事情。

对于那些想知道为什么我问上述问题的人。 我有以下功能:

 string sMessages(ArrayList aMessages, String sType) { string sReturn = String.Empty; if (aMessages.Count > 0) { sReturn += "<p class=\"" + sType + "\">"; for (int i = 0; i < aMessages.Count; i++) { sReturn += aMessages[i] + "<br />"; } sReturn += "</p>"; } return sReturn; } 

我发送一个错误消息和一个CSS类,然后返回一个网页的字符串数组。

每次我调用这个函数,我都必须定义sType。 就像是:

 output += sMessages(aErrors, "errors"); 

正如你所看到的,我的变量被称为aErrors,我的css类被称为错误。 我希望我的感冒可以根据我发送的变量名来确定要使用的类。

再次感谢所有的回应。

GateKiller,我的解决方法有什么问题? 你可以轻易地重写你的函数来使用它(我冒昧地改进功能):

 static string sMessages(Expression<Func<List<string>>> aMessages) { var messages = aMessages.Compile()(); if (messages.Count == 0) { return ""; } StringBuilder ret = new StringBuilder(); string sType = ((MemberExpression)aMessages.Body).Member.Name; ret.AppendFormat("<p class=\"{0}\">", sType); foreach (string msg in messages) { ret.Append(msg); ret.Append("<br />"); } ret.Append("</p>"); return ret.ToString(); } 

像这样调用它:

 var errors = new List<string>() { "Hi", "foo" }; var ret = sMessages(() => errors); 

做这个

 var myVariable = 123; myVariable.Named(() => myVariable); var name = myVariable.Name(); // use name how you like 

或者用手工代码来命名

 var myVariable = 123.Named("my variable"); var name = myVariable.Name(); 

使用这个类

 public static class ObjectInstanceExtensions { private static Dictionary<object, string> namedInstances = new Dictionary<object, string>(); public static void Named<T>(this T instance, Expression<Func<T>> expressionContainingOnlyYourInstance) { var name = ((MemberExpression)expressionContainingOnlyYourInstance.Body).Member.Name; instance.Named(name); } public static T Named<T>(this T instance, string named) { if (namedInstances.ContainsKey(instance)) namedInstances[instance] = named; else namedInstances.Add(instance, named); return instance; } public static string Name<T>(this T instance) { if (namedInstances.ContainsKey(instance)) return namedInstances[instance]; throw new NotImplementedException("object has not been named"); } } 

代码测试和最优雅的,我可以想出。

简短的答案是否定的,除非你真的有动力。

唯一的方法就是通过反射和堆栈走路。 你将不得不得到一个堆栈框架,找出你调用的调用函数中的位置,然后使用CodeDOM尝试找到树的正确部分,以查看表达式是什么。

例如,如果调用是ExampleFunction(“a”+“b”)呢?

好了,看了一下。 当然你不能使用任何Type信息。 此外,本地变量的名称在运行时不可用,因为它们的名称未编译到程序集的元数据中。