我正在编写一些代码,其最终目的是让您使用属性表达式来设置具有类似语法的属性的值,以将变量作为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;
}
因此,当在()=&gt; Object.Property等属性表达式上使用时,会返回&#39; Object&#39;的实例。我对使用属性表达式有点新,并且似乎有许多不同的方法来完成事情,但我想扩展到目前为止所做的事情,所以给定一个表达式如()=&gt; Foo.Bar .Baz它会给吧,而不是Foo。我总是想要表达式中的最后一个包含对象。
有什么想法吗?提前谢谢。
答案 0 :(得分:4)
您需要做的是遍历属性链到最外层的对象。 下面的示例是相当自我解释的,并表明扩展方法适用于链接字段和属性:
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));
}
}
答案 1 :(得分: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
的属性,该属性是对容器对象实例的引用。