Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

Как получить имя переменной внутри функции

#c_sharp


Как можно получить имя передаваемой переменной в вызываемом методе?
Только передавать дополнительным параметром?
Может есть какой аналог CallerMemberNameAttribute только для получения имени передаваемой
переменной?

На примере:

var variable = "some_value"; // имя переменной nameof(variable)
DoAction(variable);


public void DoAction(string value)
{
    var valueVariableName = ... // хочу получить имя переменной ("variable")
}


Буду благодарен за любые соображения.



Примечание: Изначально задумывалось сделать наподобие

public void DoAction(string value, [PreviousParameterName] string valueParameterName
= null)
{
    var valueParameterName = ... // имя переменной (== "variable")
}


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


Ответы

Ответ 1



В C# 8.0 завезут новый атрибут: CallerArgumentExpression С его помощью можно будет получить имя переменной, которое было передано. Цитирую ответ, который мне дали тут: Если метод объявлен так: public static class Debug { public static void Assert(bool condition, [CallerArgumentExpression("condition")] string message = null); } и вызывается так: Debug.Assert(someBoolean); Debug.Assert(array != null); Debug.Assert(array.Length == 1); , то компилятор подставит значение второго аргумента: Debug.Assert(someBoolean, "someBoolean"); Debug.Assert(array != null, "array != null"); Debug.Assert(array.Length == 1, "array.Length == 1"); Соответственно никаких просадок в производительности не будет, так как это будет делаться на этапе компиляции.

Ответ 2



Например, вот так: static void Main(string[] args) { var world = "Hello, {0}!"; DoAction(() => world); } static void DoAction(Expression> value) { var me = (MemberExpression)value.Body; var variableName = me.Member.Name; var variableValue = value.Compile()(); Console.WriteLine(variableValue, variableName); } Но это будет работать медленно. И точно так же будет медленно работать любая другая подобная "технология". Просто потому что уговорить компилятор сохранить имя переменной можно только превратив ее в поле или свойство - а способа быстро достать из объекта значение поля с неизвестным именем нет.

Ответ 3



Если вам имя переменной требуется не безусловно, а только в особых случаях с отладочными целями - то при наличии отладочных символов его можно попытаться достать из исходника. Например, как-то вот так: static void Main(string[] args) { string foobarbaz = null; Assert(foobarbaz != null); Assert(false); Assert(true && (false || new[] { true, false }[1])); Assert(false /* || true*/); Assert( false // || true ); Assert /*trap*/ (false); Assert($"{{" == null); } [Conditional("DEBUG"), MethodImpl(MethodImplOptions.NoInlining)] static void Assert(bool expression) { if (expression) return; string expression_raw; var frame = new StackTrace(fNeedFileInfo: true).GetFrame(1); try { using (var file = File.OpenText(frame.GetFileName())) { for (int k = frame.GetFileLineNumber() - 1; k > 0; k--) file.ReadLine(); for (int k = frame.GetFileColumnNumber() - 1; k > 0; k--) file.Read(); var parser = new Parser(file); parser.Ensure(nameof(Assert)); parser.ReadUntil('('); parser.Ensure("("); parser.BeginRecord(); parser.ReadUntil(')'); expression_raw = parser.EndRecord(); } } catch (IOException) { expression_raw = ""; } Console.WriteLine($"Assertion {expression_raw.Trim()} failed at {frame.ToString()}"); } private class Parser { private readonly TextReader input; private StringBuilder output; private char current, prev; public Parser(TextReader input) { this.input = input; } public void BeginRecord() { output = new StringBuilder(); } public string EndRecord() { var result = output.ToString(); output = null; return result; } private char Read() { var c = input.Read(); if (c == -1) throw new EndOfStreamException(); prev = current; current = (char)c; output?.Append(char.IsWhiteSpace(current) ? ' ' : current); return current; } public void Ensure(string v) { foreach (var c in v) { if (Read() != c) throw new IOException(); } } public void ReadUntil(char end) { while (input.Peek() != end) { switch (Read()) { case '(': ReadUntil(')'); Read(); break; case '[': ReadUntil(']'); Read(); break; case '{': ReadUntil('}'); Read(); break; case '\'': ReadCharacter(); if (Read() != '\'') throw new IOException(); break; case '"': ReadString(formattable: prev == '$'); Read(); break; case '/': if (prev == '/') { do { Read(); } while (current != '\n' && current != '\r'); } break; case '*': if (prev == '/') { current = '\0'; do { Read(); } while (prev != '*' || current != '/'); current = '\0'; } break; } } } private void ReadCharacter() { if (Read() == '\\') Read(); } private void ReadString(bool formattable) { while (input.Peek() != '"') { if (formattable && input.Peek() == '{') { Read(); if (input.Peek() != '{') { ReadUntil('}'); } Read(); continue; } ReadCharacter(); } } }

Комментариев нет:

Отправить комментарий