Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用表达式从Lambda创建安全的深度属性访问器_C#_Expression Trees - Fatal编程技术网

C# 使用表达式从Lambda创建安全的深度属性访问器

C# 使用表达式从Lambda创建安全的深度属性访问器,c#,expression-trees,C#,Expression Trees,我的目标是使用Lambdas创建一个属性绑定对象,该对象可以安全地检索深层属性值。安全地说,如果以前的某个属性为null,它将返回该属性类型的默认值,而不是引发null引用异常 方法签名: public static Func<TO, TP> BuildSafeAccessor<TO, TP>(this Expression<Func<TO, TP>> propertyExpression) where TO: class { } 这里的关键观察

我的目标是使用Lambdas创建一个属性绑定对象,该对象可以安全地检索深层属性值。安全地说,如果以前的某个属性为null,它将返回该属性类型的默认值,而不是引发null引用异常

方法签名:

public static Func<TO, TP> BuildSafeAccessor<TO, TP>(this Expression<Func<TO, TP>> propertyExpression) where TO: class
{
}

这里的关键观察是,您不需要“已经完全创建了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;
}

请注意,这最多可访问
a.B
三次。如果这种特性很慢或有副作用,那可能是个问题。如果这对您来说是一个问题,我认为您需要将
Block
s与局部变量一起使用。

您是否可以明确说明:这里的问题是什么?我想知道如何构造将编译为安全访问器的表达式。我的BuildSafeAccessor方法的主体。在如何构建表达式方面,我经常遇到问题,特别是Expression.Condition(test,ifTrue,ifFalse),在这里我需要完全创建ifFalse表达式。我已经找到了,我所发现的就是用一个字符串为deep属性构建一个表达式。不会引发空引用异常的内容。如果你知道的不同,请给我指出正确的方向。太好了,非常感谢!我知道递归必须发挥作用,它只是在逃避我的掌握!
if (p.Address == null)
    return default(TP);
return p.Address.Zip;
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;
}
a => IIF((a == null), null, IIF((a.B == null), null, IIF((a.B.C == null), null, a.B.C.D)))