纸上得来终觉浅,绝知此事要躬行。

 

Expression Tree 遍历集合

场景

从接口返回的数据是集合,却是 object 类型的。这个时候需要遍历这个集合。现提供两种方法。

方法一:

因为集合是可枚举的,所以可以尝试转为 IEnumerable 类型,然后遍历即可。

static void Main(string[] args)
{
    var list = new List<Foo> {
        new Foo{ Id = 1, Name = "aaa" },
        new Foo{ Id = 2, Name = "bbb" },
        new Foo{ Id = 3, Name = "ccc" }
    };

    ReflectionCollection(list);
}

private static void ReflectionCollection(object input)
{
    var collection = input as IEnumerable;
    if (collection is not null)
    {
        var enumerator = collection.GetEnumerator();
        while (enumerator.MoveNext())
        {
            Console.WriteLine($"{JsonSerializer.Serialize(enumerator.Current)}");
        }
    }
}

 

方法二:

使用表达式树。

private static void ExpressionTreeAccessCollection(object input)
{
    var type = input.GetType();
    var inputParameterExp = Expression.Parameter(typeof(object), "input");
    var callbackParameterExp = Expression.Parameter(typeof(Action<object>), "callback");
    var countVariableExp = Expression.Variable(typeof(int), "count");
    var tempVariableExp = Expression.Variable(typeof(int));
    var itemVariableExp = Expression.Variable(typeof(object), "item");
    var convertExp = Expression.Convert(inputParameterExp, type);
    var voidLabel = Expression.Label();

    // 方法一
    var indexProperty = type.GetDefaultMembers().OfType<PropertyInfo>()
        .First(_ => _.GetIndexParameters().Any(_ => _.ParameterType == typeof(int)));

    // 方法二
    //var toArrayMethod = type.GetMethod(nameof(List<object>.ToArray));
    //var toArrayExp = Expression.Call(convertExp, toArrayMethod);
    //var arrayIndexExp = Expression.ArrayIndex(toArrayExp, new Expression[] { tempVariableExp });

    // 调用外部方法
    //var printItemMethod = typeof(Program).GetMethod(nameof(PrintItem), BindingFlags.NonPublic | BindingFlags.Static);

    var blockExp = Expression.Block(
        new ParameterExpression[] { countVariableExp, tempVariableExp, itemVariableExp },
        Expression.Assign(
            countVariableExp,
            Expression.Property(convertExp, "Count")
        ),
        Expression.Assign(tempVariableExp, Expression.Constant(0)),
        Expression.Loop(
            Expression.IfThenElse(
                Expression.LessThan(tempVariableExp, countVariableExp),
                Expression.Block(
                    new ParameterExpression[] { itemVariableExp },
                    // 方法一
                    Expression.Assign(itemVariableExp, Expression.MakeIndex(convertExp, indexProperty, new ParameterExpression[] { tempVariableExp })),

                    // 方法二
                    //Expression.Assign(itemVariableExp, arrayIndexExp),

                    // 调用外部方法
                    //Expression.Call(null, printItemMethod, itemVariableExp),

                    // 调用回调函数
                    Expression.Invoke(callbackParameterExp, itemVariableExp),
                    Expression.AddAssign(tempVariableExp, Expression.Constant(1, typeof(int)))
                ),
                Expression.Block(
                    Expression.Return(voidLabel)
                )
            )
        ),
        Expression.Label(voidLabel)
    );

    var lambda = Expression.Lambda<Action<object, Action<object>>>(blockExp, new ParameterExpression[] { inputParameterExp, callbackParameterExp });
    var func = lambda.Compile();
    func(input, item => {
        Console.WriteLine($"Callback: {JsonSerializer.Serialize(item)}");
    });
}

private static void PrintItem(object item)
{
    Console.WriteLine($"PrintItem: {JsonSerializer.Serialize(item)}");
}

 

posted on 2021-08-27 08:51  JRoger  阅读(75)  评论(0编辑  收藏  举报

导航