【问题标题】:How can I get object instance from ()=>foo.Title expression如何从 ()=>foo.Title 表达式中获取对象实例
【发布时间】:2011-07-02 19:39:42
【问题描述】:

我有一个带有属性的简单类

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() 并在我的BindToText 函数中创建一个使用foo 实例的委托。所以我的问题是,如果不在函数参数中添加对实例的引用,是否可以做到这一点。我呼吁Occum's Razor 提供最简单的解决方案。

编辑 2: 许多人没有注意到的是 closure 存在于访问我的函数内部的 foo 的实例,如果我编译 lambda。为什么编译器知道在哪里可以找到实例,而我却不知道?我坚持必须有一个答案, 不必传递额外的参数。


解决方案

感谢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, () =&gt; foo.Title);

【问题讨论】:

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


    【解决方案1】:

    您想要的小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);
    }
    

    【讨论】:

    • 我猜是一些神奇的 LINQPad 方法。您可能可以直接返回或打印。
    • +1 让我更接近。唉,constantInstanceExpression.Value 返回表单的实例,而不是表单中的字段foo。如果表单中有多个用于绑定的类怎么办?我不想静态绑定到foo,而是绑定到属性中使用的任何内容。
    • 在示例中添加了您可能想要的所有内容:D 甚至手动解释表达式以获取字段值:D
    • [v/] 就是这样!现在我有一个简单的方法来做 DataBinding...太棒了
    【解决方案2】:

    不要。只需修改方法以获取另一个参数,如#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 );
    

    仍然很好,但很容易理解。没有魔法发生。 ;)

    【讨论】:

    • 谢谢,但我想避免这样做。看看我上面的编辑,看看应该有办法得到它,我只是不知道怎么做。
    【解决方案3】:

    类似下面的东西应该可以工作:

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

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

    【讨论】:

    • 糟糕,刚刚意识到这或多或少与 VisualBlackFox 的答案相同......
    【解决方案4】:

    嗯,这在语气上与 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 属性的 Binding,您可以将其添加到任何 Bindings 集合中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-08
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多