Typed Data Binding using Expression<T>
Use of System.Linq.Expression seems to be gaining momentum as a general-purpose tool for .NET programming. The first use that came to mind for me was as a mechanism for typed data binding. It has always bothered me that databinding mechanisms rely on the fragile (and unverifiable at compile-time) practice of passing the member in by name:
With the use of a fairly simple extension method, this can be made type-safe:
Here's the helper method:
Not sure whether this idea is fully-baked. I'm a little worried about the profusion of type metadata that using generics like this will produce (remember C++ "template bloat"?). Of course, this method is not truly type-safe, since it is fully possible to produce an incompatible lambda expression which will compile but will then fail to "parse" as the right expression tree at runtime. This seems less likely to me to happen than the original property-as-string being wrong.
Update: The problem with this approach is the code which attempts to access the instance of the bound object. It only works for a very small context, when the lambda expression parses as a "ConstantExpression". This is not true in many cases (like if you are trying to bind to property of an object which is itself a member of your form class.) Still trying to wrap my head around this.
Update 2: It's clear that trying to convert between expression-based binding and the reflection-based binding framework exposed by Winforms is problematic. The real solution is an end-to-end expression-based framework which binds directly to getter/setter delegates rather than taking text-based names.
name_textbox.DataBindings.Add("Text", person, "Name");
With the use of a fairly simple extension method, this can be made type-safe:
name_textbox.Bind( () => name_textbox.Text, person, () => person.Name );
Here's the helper method:
public static void Bind<ControlType, TSet, TGet>(
this ControlType control,
Expression<Func<TSet>> propSetter,
object getter,
Expression<Func<TGet>> propGetter) where ControlType : Control
{
control.DataBindings.Add(
_memberName(propSetter), getter, _memberName(propGetter));
}
private static string _memberName<TDelegate>(
Expression<TDelegate> expr)
{
var memberExpr = expr.Body as MemberExpression;
if (memberExpr == null)
{
throw new ArgumentException(
"Expected a member-accessor expression.");
}
return memberExpr.Member.Name;
}
Not sure whether this idea is fully-baked. I'm a little worried about the profusion of type metadata that using generics like this will produce (remember C++ "template bloat"?). Of course, this method is not truly type-safe, since it is fully possible to produce an incompatible lambda expression which will compile but will then fail to "parse" as the right expression tree at runtime. This seems less likely to me to happen than the original property-as-string being wrong.
Update: The problem with this approach is the code which attempts to access the instance of the bound object. It only works for a very small context, when the lambda expression parses as a "ConstantExpression". This is not true in many cases (like if you are trying to bind to property of an object which is itself a member of your form class.) Still trying to wrap my head around this.
Update 2: It's clear that trying to convert between expression-based binding and the reflection-based binding framework exposed by Winforms is problematic. The real solution is an end-to-end expression-based framework which binds directly to getter/setter delegates rather than taking text-based names.
Comments