Как я могу получить экземпляр объекта из выражения() => 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);
,
Ответы
Ответ 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.