Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine method parameter's context

Here's a challenging one. Is it possible, using any method whatsoever, to implicitly determine the name of the property that is passed as a parameter to a method?

(This may at first appearance seem like a duplicate of another question, but there is a subtle but important different insofar that we are always working with properties, which is key).

Here is the example scenario:

    public class Foo
    {
        public string Bar { get; set; }
    }

    public void SomeStrangeMethod()
    {
        Foo foo = new Foo() { Bar = "Hello" };
        string result = FindContext(foo.Bar);  // should return "Bar"
    }

    public string FindContext(object obj)
    {
        // TODO? - figure out the property name corresponding to the passed parameter.  
        // In this example, we need to somehow figure out that the value of "obj"
        // is the value of the property foo.Bar, and return "Bar"            
    }

Assume that from within FindContext, the passed parameter will always be a property of an object. The catch is, we don't know what object.

Obviously, the problem can be easily solved by passing a second parameter that provides the missing context, i.e...

FindContext(foo, foo.Bar);    
FindContext("Bar", foo.Bar);  

....but that's not what I want. I want to be able to pass a single parameter and determine the property name represented by the value.

I understand that when the parameter is passed, the method context for FindContext does not contain sufficient information to determine this. However, using some sleight of hand around stack traces and IL, perhaps we can still do it. The reason why I think this must be possible is:

  1. It is a requirement that the parameter passed to FindContext must always be a property of another object, and we know that it is possible to get said property names using reflection.

  2. Using the StackTrace, we can get the calling context.

  3. Out of the calling context, we should be able to somehow locate the symbol being used.

  4. From that symbol, we should either be able to retrieve the property name, and/or the calling object's type, which via (1) we should be able to transform into the property of the calling object.

Does anybody know how to do this? Note: This question is difficult, but I do not believe it is impossible. I will not accept any answers of "impossible" unless someone can demonstrate why it is impossible.

like image 220
Dan McCann Avatar asked Feb 18 '23 02:02

Dan McCann


1 Answers

If you pass a lambda expression you can

public class Foo
{
    public string Bar { get; set; }
}

public void SomeStrangeMethod()
{
    Foo foo = new Foo() { Bar = "Hello" };
    string result = GetName(()=>foo.Bar);  // should return "Bar"
    Debug.WriteLine(result); // "Bar"
}


public static string GetName<T>(Expression<Func<T>> expression)
{
    return ExtractPropertyName(expression);
}

/// <summary>
/// Extracts the name of a property from a suitable LambdaExpression.
/// </summary>
/// <param name="propertyExpression">The property expression.</param>
/// <returns></returns>
public static string ExtractPropertyName(LambdaExpression propertyExpression)
{
    if (propertyExpression == null)
    {
        throw new ArgumentNullException("propertyExpression");
    }

    var memberExpression = propertyExpression.Body as MemberExpression;
    if (memberExpression == null)
    {
        throw new ArgumentException(@"Not a member expression", "propertyExpression");
    }

    var property = memberExpression.Member as PropertyInfo;
    if (property == null)
    {
        throw new ArgumentException(@"Not a property", "propertyExpression");
    }

    var getMethod = property.GetGetMethod(true);
    if (getMethod.IsStatic)
    {
        throw new ArgumentException(@"Can't be static", "propertyExpression");
    }

    return memberExpression.Member.Name;
}
like image 68
Phil Avatar answered Feb 28 '23 07:02

Phil