Поиск имени переменной, переданной функции - PullRequest
50 голосов
/ 16 сентября 2008

Позвольте мне использовать следующий пример, чтобы объяснить мой вопрос:

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

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(WhatIsMyName);

Когда я передаю переменную "WhatIsMyName" в пример функции, я хочу иметь возможность получить строку с именем исходного переменной. Возможно что-то вроде:

Variable.OriginalName.ToString()

Есть ли способ сделать это?

Ответы [ 16 ]

54 голосов
/ 16 сентября 2008

То, что вы хотите, невозможно напрямую, но вы можете использовать выражения в 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 #, нет гарантии, что это не перестанет работать в будущих версиях. 1006 *

32 голосов
/ 31 августа 2015

Я знаю, что это старый вопрос, но в 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;
}
17 голосов
/ 13 декабря 2008
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;
}

Подробнее в этом сообщении в блоге .

12 голосов
/ 03 февраля 2013

Три способа:

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 снова вялый.

4 голосов
/ 19 января 2014

Да! Это возможно. Я долго искал решение этой проблемы и, наконец, придумал хак, который ее решает (это немного неприятно). Я бы не рекомендовал использовать это как часть вашей программы, и я думаю, что она работает только в режиме отладки. Для меня это не имеет значения, поскольку я использую его только как инструмент отладки в своем классе консоли, поэтому я могу сделать:

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;
        }
    }
4 голосов
/ 16 сентября 2008

Нет, но всякий раз, когда вы обнаруживаете, что делаете очень сложные вещи, подобные этой, вы можете пересмотреть свое решение. Помните, что код должен быть легче читать, чем писать.

2 голосов
/ 07 ноября 2013

Ну попробуй этот класс Utility,

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;
    }
2 голосов
/ 16 сентября 2008

System.Environment.StackTrace выдаст вам строку, содержащую текущий стек вызовов. Вы можете проанализировать это, чтобы получить информацию, которая включает имена переменных для каждого вызова.

0 голосов
/ 06 июня 2014

Сделай это

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

Код проверен и самый элегантный из всех, что я могу придумать.

0 голосов
/ 16 сентября 2008

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...