表达式树:引用但未定义的变量

时间:2018-01-06 00:55:23

标签: c# expression

我正在构建一个表达式来动态构建一些代码,以便为我的模型对象进行高效的手动JSON序列化,而无需在我更改模型时随时更新它。我的表达式在下面抛出以下异常。

在分配变量之前,我是否需要首先声明变量?

System.InvalidOperationException:'变量' sw'类型' System.IO.StringWriter'引用范围'',但未定义'

public static Func<T, string> ConstructJsonParserFunction<T>()
    {
        List<Expression> methodBodyExpressions = new List<Expression>();
        ParameterExpression methodParameter = Expression.Parameter(typeof(T), "entity");
        ParameterExpression stringWriterExpression = Expression.Variable(typeof(StringWriter), "sw");
        ParameterExpression jsonTextWriterExpression = Expression.Variable(typeof(JsonTextWriter), "writer");
        ConstructorInfo jsonTextWriterConstructor = typeof(JsonTextWriter).GetConstructor(new Type[] { typeof(TextWriter) });
        MethodInfo jsonTextWriterMethod_WriteStartObject = typeof(JsonTextWriter).GetMethod("WriteStartObject");
        MethodInfo jsonTextWriterMethod_WritePropertyName = typeof(JsonTextWriter).GetMethods()
            .Where(mi => mi.Name == "WritePropertyName" && mi.GetParameters().Length == 1 && mi.GetParameters()[0].Name == "name")
            .First();

        Dictionary<Type, MethodInfo> jsonTextWriterMethods_WriteValue = new Dictionary<Type, MethodInfo>();

        foreach (MethodInfo method in typeof(JsonTextWriter).GetMethods().Where(mi => mi.Name == "WriteValue" && mi.GetParameters().Length == 1))
        {
            jsonTextWriterMethods_WriteValue[method.GetParameters()[0].ParameterType] = method;
        }

        MethodInfo jsonTextWriterMethod_WriteEndObject = typeof(JsonTextWriter).GetMethod("WriteEndObject");
        MethodInfo stringWriterMethod_ToString = typeof(StringWriter).GetMethods()
            .Where(mi => mi.Name == "ToString" && mi.GetParameters().Length == 0)
            .First();

        methodBodyExpressions.Add(stringWriterExpression);
        methodBodyExpressions.Add(jsonTextWriterExpression);
        methodBodyExpressions.Add(Expression.Assign(stringWriterExpression, Expression.New(typeof(StringWriter))));
        methodBodyExpressions.Add(Expression.Assign(jsonTextWriterExpression, Expression.New(jsonTextWriterConstructor, stringWriterExpression)));
        methodBodyExpressions.Add(Expression.Call(jsonTextWriterExpression, jsonTextWriterMethod_WriteStartObject));

        foreach (PropertyInfo property in typeof(T).GetProperties().Where(p => Attribute.IsDefined(p, typeof(JsonPropertyAttribute))))
        {
            methodBodyExpressions.Add(Expression.Call(jsonTextWriterExpression, jsonTextWriterMethod_WritePropertyName, Expression.Constant(property.Name)));
            methodBodyExpressions.Add(Expression.Call(jsonTextWriterExpression, jsonTextWriterMethods_WriteValue[property.PropertyType], Expression.Property(methodParameter, property)));
        }

        methodBodyExpressions.Add(Expression.Call(jsonTextWriterExpression, jsonTextWriterMethod_WriteEndObject));
        methodBodyExpressions.Add(Expression.Call(stringWriterExpression, stringWriterMethod_ToString));

        BlockExpression block = Expression.Block(methodBodyExpressions);

        return Expression.Lambda<Func<T, string>>(block, methodParameter).Compile();
    }

调试视图字符串:

.Block() {
    $sw;
    $writer;
    $sw = .New System.IO.StringWriter();
    $writer = .New Newtonsoft.Json.JsonTextWriter($sw);
    .Call $writer.WriteStartObject();
    .Call $writer.WritePropertyName("CompanyId");
    .Call $writer.WriteValue($entity.CompanyId);
    .Call $writer.WritePropertyName("Name");
    .Call $writer.WriteValue($entity.Name);
    .Call $writer.WriteEndObject();
    .Call $sw.ToString()
}

修改

解决方案:正如NetMage所述,局部变量在分配之前需要两件事。首先,使用Expression.Variable(type, "debugVarName")创建ParameterExpression。其次,Expression.Block应该将SenExpressions与正文分开传递。

1 个答案:

答案 0 :(得分:1)

您创建了一个sw参数stringWriterExpression(应该称之为stringWriterParameter)并且您在身体中使用了它,但是您没有将其定义为lambda参数。

我认为您需要使用Expression.Variable代替Expression.Parameter,并将其作为ParameterExpression[]添加到您的阻止。