如何从()=> foo.Title表达式获取对象实例

时间:2011-02-23 14:45:45

标签: c# data-binding .net-3.5 expression-trees lambda

我有一个带属性的简单类

class Foo 
{ 
    string Title { get; set; } 
}

我试图通过调用类似

的函数来简化数据绑定
BindToText(titleTextBox, ()=>foo.Title );

声明为

void BindToText<T>(Control control, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;

    control.DataBindings.Add("Text", ??? , name);
}

那么我应该为???类的实例添加Foo。如何从lambda表达式中获取对调用foo实例的引用?

修改 实例应该在某处,因为我可以调用property.Compile()并创建一个使用foo函数内BindToText实例的委托。所以我的问题是,如果可以在不添加函数参数中实例的引用的情况下完成此操作。我呼吁Occum's Razor提出最简单的解决方案。

编辑2: 许多人没注意到的是,如果我编译lambda,在访问函数内foo的实例时存在的闭包。为什么编译器知道在哪里找到实例,我不知道?我坚持认为必须有一个答案,没有必须通过额外的论证。


解决方案

感谢VirtualBlackFox解决方案是这样的:

void BindText<T>(TextBoxBase text, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;
    var fex = mex.Expression as MemberExpression;
    var cex = fex.Expression as ConstantExpression;            
    var fld = fex.Member as FieldInfo;
    var x = fld.GetValue(cex.Value);
    text.DataBindings.Add("Text", x, name);            
}

让我只需输入BindText(titleText, () => foo.Title);

4 个答案:

答案 0 :(得分:16)

您想要的小LINQPad样本:

void Foo<T>(Expression<Func<T>> prop)
{
    var propertyGetExpression = prop.Body as MemberExpression;

    // Display the property you are accessing, here "Height"
    propertyGetExpression.Member.Name.Dump();

    // "s" is replaced by a field access on a compiler-generated class from the closure
    var fieldOnClosureExpression = propertyGetExpression.Expression as MemberExpression;

    // Find the compiler-generated class
    var closureClassExpression = fieldOnClosureExpression.Expression as ConstantExpression;
    var closureClassInstance = closureClassExpression.Value;

    // Find the field value, in this case it's a reference to the "s" variable
    var closureFieldInfo = fieldOnClosureExpression.Member as FieldInfo;
    var closureFieldValue = closureFieldInfo.GetValue(closureClassInstance);

    closureFieldValue.Dump();

    // We know that the Expression is a property access so we get the PropertyInfo instance
    // And even access the value (yes compiling the expression would have been simpler :D)
    var propertyInfo = propertyGetExpression.Member as PropertyInfo;
    var propertyValue = propertyInfo.GetValue(closureFieldValue, null);
    propertyValue.Dump();
}

void Main()
{
    string s = "Hello world";
    Foo(() => s.Length);
}

答案 1 :(得分:4)

别。只需修改方法以获取另一个参数,如#3444294中所述。对于您的示例,它可能是这样的:

void BindToText<T>(Control control, T dataSource, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;

    control.DataBindings.Add("Text", dataSource, name);
}

并且会被称为

BindToText(titleTextBox, foo, ()=>foo.Title );

还不错,但很容易理解。没有魔法发生。 ;)

答案 2 :(得分:2)

以下内容应该有效:

void BindToText<T>(Control control, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;

    var fooMember = mex.Expression as MemberExpression;
    var fooConstant = fooMember.Expression as ConstantExpression;
    var foo = fooConstant.Value;

    control.DataBindings.Add("Text", foo, name);
}

如果这对您不起作用,请告诉我。

答案 3 :(得分:1)

嗯,这与Hangy的解决方案类似,但我觉得使用起来相当舒服并且不需要太多魔术:

public static Binding CreateTextBinding<T>(this T source, Expression<Func<T,object>> access)
{
    var mex = access.Body as MemberExpression;
    string name = mex.Member.Name;
    return new Binding("Text", source, name);
}

这基本上是一个扩展方法,可以在任何充当源的对象上调用。它为您提供了一个Text属性的绑定,您可以将其添加到任何Bindings集合。