Подтвердить что ты не робот

Как я могу получить экземпляр объекта из выражения() => foo.Title

У меня есть простой класс с свойством

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. Как получить ссылку на вызывающий экземпляр foo из выражения лямбда?

edit: экземпляр должен быть где-то, потому что я могу вызвать property.Compile() и создать делегат, который использует экземпляр foo внутри моей функции BindToText. Поэтому мой вопрос в том, можно ли это сделать без добавления ссылки на экземпляр в параметрах функции. Я призываю Occum Razor дать простейшее решение.

edit 2: Многие из них не заметили закрытие, которое существует при доступе к экземпляру 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); ,

4b9b3361

Ответ 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 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);
}

Ответ 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);
}

Сообщите мне, если это не сработает для вас.

Ответ 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);
}

Это в основном метод расширения, который можно вызвать для любого объекта, действующего в качестве источника. Он возвращает вам свойство Binding for the Text, которое вы можете добавить в любую коллекцию Bindings.