5

我正在编写一些代码,其最终目的是让您使用属性表达式来设置属性的值,其语法类似于将变量作为 out 或 ref 参数传递。

类似于以下内容:

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

并且 Object.Property 将被赋予 value 的值。

我正在使用以下代码来获取属性的拥有对象:

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;
    }

因此,当用于 ()=>Object.Property 之类的属性表达式时,这将返回“Object”的实例。我对使用属性表达式有点陌生,似乎有很多不同的方法来完成事情,但我想扩展我到目前为止所拥有的,所以给定一个表达式,例如 ()=>Foo.Bar.Baz将给 Bar,而不是 Foo。我总是想要表达式中的最后一个包含对象。

有任何想法吗?提前致谢。

4

2 回答 2

5

你要做的就是遍历属性链到最外层的对象。下面的示例是不言自明的,它表明扩展方法适用于链式字段和属性:

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));
    }
}
于 2014-01-03T23:00:19.223 回答
0

如果您的项目是 MVC 5 项目并且您有对程序集的引用,System.Web.Mvc您可以使用以下内容:

前段时间我写了一个扩展方法来轻松地创建一个多选下拉菜单(基于 bootstrap 4),它看起来像这样:

public static MvcHtmlString MultiSelectFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
/*The challenge I faced here was that the expression you passed could very well be nested, so in order overcome this, I decompiled the dll to see how MVC does it, and I found this piece of code.*/

 string expressionText = System.Web.Mvc.ExpressionHelper.GetExpressionText((LambdaExpression)expression);
 System.Web.Mvc.ModelMetadata metadata = System.Web.Mvc.ModelMetadata.FromStringExpression(expressionText, htmlHelper.ViewData);

}

metadata对象有一个名为的属性PropertyName和另一个名为的属性,该属性Container是对容器对象实例的引用。

于 2019-04-17T11:58:17.680 回答