Checking property name at compile time in lambda expression
In a previous question of mine, Linq expressions and extension methods to get property name I asked about data binding between two POCO properties using expressions and extensions. I got a helpful anser and it is working fine but I have a question about it.
Here is the code:
public static class Extensions
{
public static void Bind<TSourceProperty, TDestinationProperty>(this INotifyPropertyChanged source, Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
{
var expressionDetails = GetExpressionDetails<TSourceProperty, TDestinationProperty>(bindExpression);
var sourcePropertyName = expressionDetails.Item1;
var destinationObject = expressionDetails.Item2;
var destinationPropertyName = expressionDetails.Item3;
// Do binding here
Console.WriteLine("{0} {1}", sourcePropertyName, destinationPropertyName);
}
private static Tuple<string, INotifyPropertyChanged, string> GetExpressionDetails<TSourceProperty, TDestinationProperty>(Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
{
var lambda = (LambdaExpression)bindExpression;
ParameterExpression sourceExpression = lambda.Parameters.FirstOrDefault();
MemberExpression destinationExpression = (MemberExpression)lambda.Body;
var memberExpression = destinationExpression.Expression as MemberExpression;
var constantExpression = memberExpression.Expression as ConstantExpression;
var fieldInfo = memberExpression.Member as FieldInfo;
var destinationObject = fieldInfo.GetValue(constantExpression.Value) as INotifyPropertyChanged;
return new Tuple<string, INotifyPropertyChanged, string>(sourceExpression.Name, destinationObject, destinationExpression.Member.Name);
}
}
Usage:
public class TestSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
}
public class TestDestination : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Id { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new TestSource(开发者_开发百科);
var y = new TestDestination();
x.Bind<string, string>(Name => y.Id);
}
}
My questions regarding the above are:
When I make the call to Bind, the second parameter is a member of the current class, so I have something like x.Bind(Name => Id); instead of x.Bind(Name => y.Id). In this case, the Bind fails since destinationExpression.Expression is a ConstantExpression instead of a MemberExpression. I am not sure what I would need to change to make it work in that case.
Is there a way to make it fail at compile time if a property name is incorrect, for example x.Bind(Na123me => Id)?
No. The fact is, you're just using a trick to make it easier to produce an expression like this. But there is no way to enforce that the lambda expression follows a particular pattern at compile time. That's why technologies like LINQ to Entities tend to produce a lot of runtime exceptions.
精彩评论