it-swarm-ru.tech

Поиск имени переменной, переданной функции

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

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

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

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

Variable.OriginalName.ToString()

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

50
GateKiller

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

54
Konrad Rudolph

Я знаю, что это старый вопрос, но в 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;
}
32
johnny 5
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;
}

Более подробная информация в это сообщение в блоге .

17
Rinat Abdullin

Три способа:

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

12
nawfal

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

4
Nate Kohari

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

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
blooop

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

2
kevin42

Хорошо попробуй этот класс 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
Dipon Roy

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);
0
Konrad Rudolph

Спасибо за все ответы. Я думаю, мне просто нужно идти с тем, что я делаю сейчас.

Для тех, кто хотел знать, почему я задал вышеуказанный вопрос. У меня есть следующая функция:

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 - ошибками. Я надеялся, что моя простуда сможет выяснить, какой класс использовать на основе имени переменной, которую я ему отправил.

Еще раз спасибо за все ответы.

0
GateKiller

Сделай это

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
kernowcode

Нет. Ссылка на вашу строковую переменную передается в функцию - в нее не включены никакие внутренние метадеты. Даже рефлексия не сможет вытащить вас из леса - работа в обратном направлении от одного ссылочного типа не даст вам достаточно информации, чтобы сделать то, что вам нужно.

Лучше вернитесь к чертежной доске на этом

р.П.

0
rp.

Вы можете использовать отражение, чтобы получить все свойства объекта, а затем пройти через него и получить значение свойства, где имя (свойства) совпадает с переданным параметром in.

0
Adam Vigh