c# - 为什么不抛出表达式树中最里面的异常?

标签 c# expression-trees expressionvisitor

我一直在尝试创建一个自定义 ExpressionVisitor,它会生成一个表达式(可选)在第一个 null 上抛出一个 NullReferenceException值(value)。表达式的 DebugView 对我来说看起来不错,但它不能作为 exptecte(对我而言)。我以为它会先抛出

.Throw .New System.NullReferenceException("c3")

因为测试变量是null 但是这个变量被抛出

.Throw .New System.NullReferenceException("p")

我不明白为什么它向后执行语句。不是应该先执行最里面的If吗?

调试 View :

.Block() {
    .If (.Block() {
        .If (.Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0) == null) {
            .Throw .New System.NullReferenceException("c3")
        } .Else {
            .Default(System.Void)
        };
        .Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0).c3
    } == null) {
        .Throw .New System.NullReferenceException("p")
    } .Else {
        .Default(System.Void)
    };
    (.Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0).c3).p
}

我的完整测试代码:

namespace ExpressionTrees
{
    class c1
    {
        public c2 c2 { get; set; }
    }

    class c2
    {
        public c3 c3 { get; set; }
    }

    class c3
    {
        public string p { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            c3 c3 = null; 
            var test_c3 = NullGuard.Check(() => c3.p, true);
        }
    }

    public static class NullGuard
    {
        public static T Check<T>(Expression<Func<T>> expression, bool canThrowNullReferenceException = false)
        {
            var nullGuardVisitor = new NullGuardVisitor(canThrowNullReferenceException);
            var nullGuardExpression = nullGuardVisitor.Visit(expression.Body);            
            var nullGuardLambda = Expression.Lambda<Func<T>>(nullGuardExpression, expression.Parameters);
            var value = nullGuardLambda.Compile()();
            return value;
        }
    }

    public class NullGuardVisitor : ExpressionVisitor
    {
        private readonly bool _canThrowNullReferenceException;

        internal NullGuardVisitor(bool canThrowNullReferenceException)
        {
            _canThrowNullReferenceException = canThrowNullReferenceException;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            var expression = Visit(node.Expression);

            // expression == null
            var expressionEqualsNull = Expression.Equal(expression, Expression.Constant(null, expression.Type));

            if (_canThrowNullReferenceException)
            {
                var nullReferenceExceptionConstructorInfo = typeof(NullReferenceException).GetConstructor(new[] { typeof(string) });

                // if (expression == null) { throw new NullReferenceException() } else { node }
                var result = 
                    Expression.Block(
                        Expression.IfThen(
                            expressionEqualsNull,
                            Expression.Throw(Expression.New(nullReferenceExceptionConstructorInfo, Expression.Constant(node.Member.Name)))
                        ),
                        node
                    );
                return result;
            }
            else
            {
                var result = Expression.Condition(
                    expressionEqualsNull,
                    Expression.Constant(null, expression.Type),
                    node);
                return result;
            }

        }
    }
}

最佳答案

它按预期工作。

这是具有不同空白间距和行号的相同调试 View :

1   .Block() 
2   {
3       .If (.Block() 
4       {
5           .If (.Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0) == null) 
6           {
7               .Throw .New System.NullReferenceException("c3")
8           } 
9           .Else 
10          {
11              .Default(System.Void)
12          };
13          .Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0).c3
14      } == null) 
15      {
16          .Throw .New System.NullReferenceException("p")
17      } .Else 
18      {
19          .Default(System.Void)
20      };
21      (.Constant<ExpressionTrees.Program+<>c__DisplayClass0_0>(ExpressionTrees.Program+<>c__DisplayClass0_0).c3).p
22  }

请看第 13 行:它实际上说 if ((closure class).c3 == null) throw new NullReferenceException("p")。而您的第一个检查(第 5 行)实际上是说 if ((closure class) == null) throw new NullReferenceException("c3")。问题出在误导性异常消息中。

关于c# - 为什么不抛出表达式树中最里面的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34777528/

相关文章:

c# - Xamarin:创建自定义 GridView

c# - 如何从表达式树中获取参数名称?

c# - 使用实体属性 (x.ID == 123) 创建表达式树 (Expression<Func<TEntity, bool>>)

C# 表达式访问者,如何否定构建过滤器

c# - 如何计算传输速率speed kb/s SOCKETS c#

c# - 确定系统架构

c# - 将批处理事件发送到 azure eventhub 时失败

c# - 参数化 Linq 表达式帮助

c# - 如何将表达式 x=>!x 重写为 x=>x!=true 并将 x=>x 重写为 x=>x==true