代码之家  ›  专栏  ›  技术社区  ›  GateKiller

查找传递给函数的变量名

c#
  •  46
  • GateKiller  · 技术社区  · 16 年前

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

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

    当我将变量“whatismyname”传递给示例函数时,我希望能够获得原始变量名的字符串。可能有点像:

    Variable.OriginalName.ToString()
    

    有什么办法吗?

    16 回复  |  直到 8 年前
        1
  •  -7
  •   Gishu    16 年前

    不,我不这么认为。

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

    如果它有帮助,您可以用属性name和param定义一个名为namedparameter的新类。然后将这个对象作为参数传递。

        2
  •  53
  •   Konrad Rudolph    11 年前

    您想要的是不可能直接实现的,但您可以在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编译器中工作, 在MonosC编译器中,不能保证在将来的版本中不会停止工作。

        3
  •  33
  •   johnny 5    8 年前

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

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

    表达式的名称是常量。在所有情况下,在编译时计算(…)的名称以生成字符串。它的参数不会在运行时计算,并且被认为是不可访问的代码(但是它不会发出“不可访问的代码”警告)。

    可以找到更多信息 here

    旧版本的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;
    }
    
        4
  •  17
  •   Theraot    11 年前
    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;
    }
    

    更多详细信息 this blog post .

        5
  •  11
  •   nawfal Donny V.    11 年前

    三种方式:

    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又慢了。

        6
  •  4
  •   Nate Kohari    16 年前

    不,但是当你发现自己做了如此复杂的事情时,你可能会想重新考虑你的解决方案。记住,代码应该比写代码更容易阅读。

        7
  •  4
  •   blooop    11 年前

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

    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;
            }
        }
    
        8
  •  2
  •   kevin42    16 年前

    system.environment.stacktrace将为您提供一个包含当前调用堆栈的字符串。您可以通过解析得到信息,其中包括每个调用的变量名。

        9
  •  2
  •   Dipon Roy    11 年前

    试试这个实用类,

    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;
        }
    
        10
  •  0
  •   rp.    16 年前

    不。对字符串变量的引用将传递给funcion——其中不包含任何固有的metadeta。即使是反射也不能让你走出这片树林——从一个引用类型向后工作并不能让你获得足够的信息来做你需要做的事情。

    最好回到这张画板上!

    RP

        11
  •  0
  •   Adam Vigh    16 年前

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

        12
  •  0
  •   GateKiller    16 年前

    谢谢你的回复。我想我现在要做的就是去做。

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

    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类称为errors。我希望我的感冒能根据我发送的变量名找出要使用的类。

    再次感谢所有的回复。

        13
  •  0
  •   Konrad Rudolph    16 年前

    网关杀手,我的工作区怎么了?你可以重写你的函数来使用它(我已经自由地在运行中改进了函数):

    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);
    
        14
  •  0
  •   Jay Byford-Rew    10 年前

    这样做

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

    经过代码测试,我能想到的最优雅的。

        15
  •  -1
  •   Rob Walker    16 年前

    简短的回答是不…除非你真的有动力。

    实现这一点的唯一方法是通过反射和堆栈遍历。您必须得到一个堆栈框架,计算出从中调用的调用函数的位置,然后使用codedom尝试找到树的正确部分,以查看表达式是什么。

    例如,如果调用是exampleFunction(“a”+“b”),该怎么办?

        16
  •  -1
  •   penderi    16 年前

    嗯,看了看。 当然 您不能使用任何类型信息。 此外,局部变量的名称在运行时不可用 因为它们的名称没有编译到程序集的元数据中。