這裏的關鍵是你不需要「已經完全創建了ifFalse表達式」,你可以遞歸地構建它。
的代碼看起來是這樣的:
public static Func<TO, TP> BuildSafeAccessor<TO, TP>(Expression<Func<TO, TP>> propertyExpression)
{
var properties = GetProperties(propertyExpression.Body);
var parameter = propertyExpression.Parameters.Single();
var nullExpression = Expression.Constant(default(TP), typeof(TP));
var lambdaBody = BuildSafeAccessorExpression(parameter, properties, nullExpression);
var lambda = Expression.Lambda<Func<TO, TP>>(lambdaBody, parameter);
return lambda.Compile();
}
private static Expression BuildSafeAccessorExpression(Expression init, IEnumerable<PropertyInfo> properties, Expression nullExpression)
{
if (!properties.Any())
return init;
var propertyAccess = Expression.Property(init, properties.First());
var nextStep = BuildSafeAccessorExpression(propertyAccess, properties.Skip(1), nullExpression);
return Expression.Condition(
Expression.ReferenceEqual(init, Expression.Constant(null)), nullExpression, nextStep);
}
private static IEnumerable<PropertyInfo> GetProperties(Expression expression)
{
var results = new List<PropertyInfo>();
while (expression is MemberExpression)
{
var memberExpression = (MemberExpression)expression;
results.Add((PropertyInfo)memberExpression.Member);
expression = memberExpression.Expression;
}
if (!(expression is ParameterExpression))
throw new ArgumentException();
results.Reverse();
return results;
}
(注意,此代碼使用LINQ低效,使其更具可讀性)
如果您在拉姆達a => a.B.C.D
運行它,它會創建表達式(表示ToString()
在其上的結果):
a => IIF((a == null), null, IIF((a.B == null), null, IIF((a.B.C == null), null, a.B.C.D)))
注意,這個訪問a.B
最多三次。如果該財產緩慢或有副作用,那可能是一個問題。如果這對您來說是一個問題,我認爲您需要使用Block
與局部變量。
你能明白嗎?這裏的*問題*是什麼? –
我想知道如何構建將編譯成安全訪問器的表達式。我的BuildSafeAccessor方法的正文。 –
我不斷遇到如何構建表達式的問題,特別是Expression.Condition(test,ifTrue,ifFalse),我需要已經完全創建的ifFalse表達式。可能有一些工作在一點。 –