Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the owning object of a property from a Property Expression

Tags:

c#

lambda

I'm working on a bit of code which has an end purpose of letting you use a property expression to set the value of a property with similar syntax to passing a variable as an out or ref parameter.

Something along the lines of:

public static foo(()=>Object.property, value);

And Object.Property will be assigned the value of value.

I'm using the following code to get the owining object of the property:

public static object GetOwningObject<T>(this Expression<Func<T>> @this)
    {
        var memberExpression = @this.Body as MemberExpression;
        if (memberExpression != null)
        {
            var fieldExpression = memberExpression.Expression as MemberExpression;
            if (fieldExpression != null)
            {
                var constExpression = fieldExpression.Expression as ConstantExpression;
                var field = fieldExpression.Member as FieldInfo;
                if (constExpression != null) if (field != null) return field.GetValue(constExpression.Value);
            }
        }
        return null;
    }

So this would, when used on a property expression like ()=>Object.Property, give back the instance of 'Object'. I'm somewhat new to using property expressions, and there seems to be many different ways to accomplish things, but I want to extend what I have so far, so that given an expression such as ()=>Foo.Bar.Baz it will give the Bar, not Foo. I always want the last containing object in the expression.

Any ideas? Thanks in advance.

like image 764
Brandorf Avatar asked Dec 06 '12 17:12

Brandorf


1 Answers

What you have to do is to traverse through property chain to the most outer object. The sample below is rather self explanatory and shows that the extension method would work for chained fields as well as properties:

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

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

class FooWithField
{
    public BarWithField BarField;
}

class BarWithField
{
    public string BazField;
}

public static class LambdaExtensions
{
    public static object GetRootObject<T>(this Expression<Func<T>> expression)
    {
        var propertyAccessExpression = expression.Body as MemberExpression;
        if (propertyAccessExpression == null)
            return null;

        //go up through property/field chain
        while (propertyAccessExpression.Expression is MemberExpression)
            propertyAccessExpression = (MemberExpression)propertyAccessExpression.Expression;

        //the last expression suppose to be a constant expression referring to captured variable ...
        var rootObjectConstantExpression = propertyAccessExpression.Expression as ConstantExpression;
        if (rootObjectConstantExpression == null)
            return null;

        //... which is stored in a field of generated class that holds all captured variables.
        var fieldInfo = propertyAccessExpression.Member as FieldInfo;
        if (fieldInfo != null)
            return fieldInfo.GetValue(rootObjectConstantExpression.Value);

        return null;
    }
}

[TestFixture]
public class Program
{
    [Test]
    public void Should_find_root_element_by_property_chain()
    {
        var foo = new Foo { Bar = new Bar { Baz = "text" } };
        Expression<Func<string>> expression = () => foo.Bar.Baz;
        Assert.That(expression.GetRootObject(), Is.SameAs(foo));
    }

    [Test]
    public void Should_find_root_element_by_field_chain()
    {
        var foo = new FooWithField { BarField = new BarWithField { BazField = "text" } };
        Expression<Func<string>> expression = () => foo.BarField.BazField;
        Assert.That(expression.GetRootObject(), Is.SameAs(foo));
    }
}
like image 190
Wojciech Kotlarski Avatar answered Sep 19 '22 06:09

Wojciech Kotlarski