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

<p>позвольте мне использовать следующий пример, чтобы объяснить мой вопрос:</p><pre><code>public string examplefunction(string variable) { return something; } string whatismyname = "hello world"'; string hello = examplefunction(whatismyname); </code></pre><p>когда я передаю переменную "whatismyname" в пример функции, я хочу иметь возможность получить строку с исходным именем переменной. возможно что-то вроде:</p><pre><code>variable.originalname.tostring() </code></pre><p>есть какой-либо способ сделать это?</p>

<p>то, что вы хотите, не возможно напрямую, но вы можете использовать выражения в c # 3.0:</p><pre><code>public void examplefunction(expression<func<string, string>> f) { console.writeline((f.body as memberexpression).member.name); } examplefunction(x => whatismyname); </code></pre><p>обратите внимание, что это зависит от неопределенного поведения, и хотя оно работает в текущих компиляторах microsoft c # и vb, <strong>и</strong> в компиляторе c # mono, нет никакой гарантии, что это не перестанет работать в будущие версии.</p>

<p>я знаю, что это старый вопрос, но в c # 6.0 они вводят имя оператора, который должен решить эту проблему. имя оператора разрешает имя передаваемой в него переменной.</p><p>использование для вашего случая будет выглядеть так:</p><pre><code>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)); </code></pre><p>основным преимуществом является то, что это делается во время компиляции,</p><blockquote> <p>имя выражения является константой. во всех случаях nameof (...) вычисляется во время компиляции для получения строки. его аргумент не оценивается во время выполнения и считается недоступным кодом (однако он не выдает предупреждение "недоступный код").</p> </blockquote><p>более подробную информацию можно найти <a href="https://msdn.microsoft.com/en-us/magazine/dn802602.aspx">здесь</a> </p><p><strong> старая версия c 3.0 и выше </strong><br> чтобы построить на nawfals ответ</p><pre><code>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; } </code></pre>

<pre><code>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; } </code></pre><p>более подробная информация в <a href="http://web.archive.org/web/20130124234247/http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html">это сообщение в блоге</a> .</p>

<p>три способа:</p><p>1) нечто вообще не задумываясь</p><pre><code>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(); } </code></pre><p>2) использует отражение, но это намного быстрее, чем два других.</p><pre><code>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; } </code></pre><p>3) самый медленный из всех, не используйте.</p><pre><code>getparametername3(() => variable); public static string getparametername3<t>(expression<func<t>> expr) { if (expr == null) return string.empty; return ((memberexpression)expr.body).member.name; } </code></pre><p>чтобы получить имя и значение комбинированного параметра, вы можете расширить эти методы. конечно, легко получить значение, если передать параметр отдельно в качестве другого аргумента, но это не элегантно. вместо:</p><p>1)</p><pre><code>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(); } </code></pre><p>2)</p><pre><code>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); } </code></pre><p>3)</p><pre><code>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); } </code></pre><p>1 и 2 теперь имеют сравнимую скорость, 3 снова вяло.</p>

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

<p><strong>да!</strong> это возможно. я долго искал решение этой проблемы и, наконец, придумал хак, который ее решает (это немного неприятно). я бы не рекомендовал использовать это как часть вашей программы, и я думаю, что она работает только в режиме отладки. для меня это не имеет значения, так как я использую его только как инструмент отладки в своем классе консоли, поэтому я могу сделать:</p><pre><code>int testvar = 1; bool testboolvar = true; myconsole.writeline(testvar); myconsole.writeline(testboolvar); </code></pre><p>вывод на консоль будет:</p><pre><code>testvar: 1 testboolvar: true </code></pre><p>вот функция, которую я использую для этого (не включая код переноса для моего консольного класса).</p><pre><code> 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; } } </code></pre>

<p>system.environment.stacktrace выдаст вам строку, содержащую текущий стек вызовов. вы можете проанализировать это, чтобы получить информацию, которая включает имена переменных для каждого вызова.</p>

<p>хорошо попробуй этот класс utility,</p><pre><code>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; } } </code></pre><p>и пользователю это нравится</p><pre><code> /*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; } </code></pre>

<p>gatekiller, что не так с моим решением? вы можете переписать свою функцию тривиально, чтобы использовать ее (я позволил себе улучшить функцию на лету):</p><pre><code>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(); } </code></pre><p>назовите это так:</p><pre><code>var errors = new list<string>() { "hi", "foo" }; var ret = smessages(() => errors); </code></pre>

<p>спасибо за все ответы. я думаю, мне просто нужно идти с тем, что я делаю сейчас.</p><p>для тех, кто хотел знать, почему я задал вышеуказанный вопрос. у меня есть следующая функция:</p><pre><code>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; } </code></pre><p>я посылаю ему массив сообщений об ошибках и класс css, который затем возвращается в виде строки для веб-страницы.</p><p>каждый раз, когда я вызываю эту функцию, я должен определить stype. что-то вроде:</p><pre><code>output += smessages(aerrors, "errors"); </code></pre><p>как видите, мои переменные называются aerrors, а мой класс css - ошибками. я надеялся, что моя простуда сможет выяснить, какой класс использовать на основе имени переменной, которую я ему отправил.</p><p>еще раз спасибо за все ответы.</p>

<p>сделай это</p><pre><code>var myvariable = 123; myvariable.named(() => myvariable); var name = myvariable.name(); // use name how you like </code></pre><p>или имя в коде от руки</p><pre><code>var myvariable = 123.named("my variable"); var name = myvariable.name(); </code></pre><p>используя этот класс</p><pre><code>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"); } } </code></pre><p>код проверен и самый элегантный из всех, что я могу придумать.</p>

<p>нет. ссылка на вашу строковую переменную передается в функцию - в нее не включены никакие внутренние метадеты. даже рефлексия не сможет вытащить вас из леса - работа в обратном направлении от одного ссылочного типа не даст вам достаточно информации, чтобы сделать то, что вам нужно.</p><p>лучше вернитесь к чертежной доске на этом</p><p>р.п.</p>

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