Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>First of all you can't query the runtime for the that information magically, implicitly.</p> <p>It is true that you can implicitly learn the complete identity of the currently executing method, its caller, its caller's caller and all of the stacktrace (except for the particular generic arguments in case of generic methods) by instantiating the <code>StackTrace</code> class or by calling the <code>MethodBase.GetCurrentMethod()</code> method.</p> <p>It is also true that the resulting <code>MethodBase</code> instances hold information about the methods' parameters and so, you might be able to learn the names of the parameters, but that's where it all ends. If a mechanism allowing you to implicitly probe for parameter or local variable values would've been invented then <strong>everything</strong> would be <strong>a lot slower</strong>.</p> <p>What you <strong>can</strong> do, is help yourself, a bit, by means of the <code>Expression&lt;Lambda&gt;</code> class and its entourage. It's gonna be a bit slow, but you get to choose whether you want reviewable and easy to manage code, or misteriously hard to manage and very very fast code.</p> <p><code>Expression&lt;Lambda&gt;</code> is how LINQ manages to <strong>not</strong> do a full table scan of database tables but rather <em>understand</em> what you did with your query and translate that (at runtime) into SQL or whatever other language you might imagine (depending on the actual LINQ provider).</p> <p>First of all, I would suggest splitting concerns into 2 categories:</p> <ol> <li>Retrieving names and values (as implicitly as possible)</li> <li>Using names and values (wherever you want to)</li> </ol> <p>To make that happen you need to think about an entity which can hold the results of point 1. In my suggestion to you that would be a kind of <code>Dictionary&lt;string, object&gt;</code> but you can do whatever suits you best.</p> <p>My suggestion can be used like so:</p> <pre><code>public void SomeMethod(string x, int y) { IDictionary&lt;string, object&gt; paramValues = Helper.TapInto( () =&gt; x, () =&gt; y ); // paramValues["x"] an paramValues["y"] will hold the values of x and y } </code></pre> <p>So, on to the coding bit. You could write a <code>Helper</code> class like so:</p> <pre><code>public static class Helper { } </code></pre> <p>In that <code>Helper</code> class you could invent a static method ( I called mine <code>TapInto</code> maybe that's not the best name for it ) which receives a primitive array of <code>Expression&lt;Func&lt;object&gt;&gt;</code> instances. It does that with a <code>params</code> modifier so that you can easily pass implicit lambdas to it. As a return it gives you a hashtable from <code>string</code> to <code>object</code> representing the "decompiled" variable names and their associated values.</p> <p>In my case, I also created a private overload of that same method which is actually an "extension" method, to make the code clearer.</p> <pre><code>public static class Helper { // ... an overload of the TapInto method is about to appear right here public static IDictionary&lt;string, object&gt; TapInto(params Expression&lt;Func&lt;object&gt;&gt;[] parameterTouchers) { var result = new Dictionary&lt;string, object&gt;(); foreach (var toucher in parameterTouchers) { string name; object value; toucher.TapInto(out name, out value); result[name] = value; } return result; } </code></pre> <p>So all the public method does is iterate through the list and accumulate the yielded results into the dictionary.</p> <p>Next let's look at the real magic, which happens in the <code>toucher.TapInto(out name, out value)</code> call:</p> <pre><code>public static class Helper { private static void TapInto(this Expression&lt;Func&lt;object&gt;&gt; @this, out string name, out object value) { Expression expression = @this.Body; if (expression is UnaryExpression) expression = (expression as UnaryExpression).Operand; name = (expression as MemberExpression).Member.Name; Func&lt;object&gt; compiledLambda = @this.Compile(); value = compiledLambda(); } // ... public helper method right here } </code></pre> <p>What we're doing here is "we're looking inside" the lambda with a magnifying glass. Because we're gonna use stuff other than <code>object</code> variables it's imminent to observe an implicit conversion like</p> <pre><code>.. int someParameter .. object obj = someParameter; </code></pre> <p>which is only implicit in the actual C# code, but is actually compiled as an explicit conversion:</p> <pre><code>object obj = (object)someParameter; </code></pre> <p>But you might have a plain <code>object</code> parameter, like <code>object anotherParam</code>, in which case there would be no conversion at all.</p> <p>That is why, upon observing the expression's intricate details, I presume I might find a conversion ( represented by the <code>UnaryExpression</code> class ) or not.</p> <p>Actually it's like saying: In this particular case, my contract to the calling code is that it <em>may</em> send me only stuff which falls into these 2 categories:</p> <ol> <li>Immediate <code>object</code> variable reference: <code>() =&gt; someObjectVariable</code></li> <li>Variable reference with a conversion: <code>() =&gt; (object)x</code></li> </ol> <p>The contract also accidentally states that the "conversion" bit can be replaced by a <code>UnaryExpression</code>, for instance: <code>() =&gt; !someBool</code>.</p> <p>It also states that you cannot do something like: </p> <ul> <li><code>() =&gt; 123</code> </li> <li>or <code>() =&gt; a + b + c + 100</code></li> <li>or anything else in those directions</li> </ul> <p>So, to wrap it up:</p> <ol> <li>You could write your nice little helper</li> <li>You could use it wherever you want to use it to produce maps between param names and their values although it's not 100% implicit, <strong>but</strong> at least it won't compile if you rename a parameter without complete refactoring or it will let you rename the parameter references if you choose to rename the parameters using refactoring (it also works on fields, local variables, etc)</li> <li>Pass your dictionaries in between parts of your code that are interested in them and use them accordingly !</li> </ol>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload