我一直在尝试创建一个自定义ExpressionVisitor
,该自定义会生成一个(可选)NullReferenceException
在第一个null
值上抛出 a 的表达式。表达式的DebugView
对我来说看起来不错,但它不能作为 exptecte (由我)工作。我以为它会首先抛出
.Throw .New System.NullReferenceException("c3")
因为测试变量是null
但是这个变量被抛出
.Throw .New System.NullReferenceException("p")
我不明白为什么它会向后执行语句。它不应该If
先执行最里面的吗?
调试视图:
.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;
}
}
}
}