采用表达式树(Expression Block)对一个对象的属性进行“遍历”

原创
2021/05/04 09:56
阅读数 61

文章阅读顺序建议:
本系列有一个递进的顺序,可依次阅读以下的文章:
一、采用Delegate对一个未知类型的对象进行"遍历"
http://blog.csdn.net/kmguo/article/details/17392185
二、采用表达式树(Expression Tree)对一个对象的属性进行“遍历”
http://blog.csdn.net/kmguo/article/details/19975331
三、 采用表达式树(Expression Block)对一个对象的属性进行“遍历”
  http://blog.csdn.net/kmguo/article/details/20376187


在文章《采用表达式树(Expression Tree)对一个对象的属性进行“遍历”》一文中,虽然能够遍历一个”嵌套“的对象,但是当一个对象中嵌套的另一个对象为null时,将会报错:NullReferenceException等。举例说明:
public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Location Location { get; set; }
    } 
 
 
    public class Location 
    { 
        public int Row { get; set; } 
        public int Col { get; set; } 
    }


如:
    var st = new Student(); 
文章《采用表达式树(Expression Tree)对一个对象的属性进行“遍历”》想要访问Location的内部属性时,将会报错,原因是:它采用的访问方式类似于:
    st => st.Location.Row
由于此时的st.Location=null,因此,访问会出错。

本文的目的是要动态地对Location是否为null进行检测,如果为空,则下一个层次的数据将访问默认值(当然,也可以由用户预先输入的默认值)
一、前提知识:
Expression (Block):http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.block(v=vs.110).aspx
参考: http://www.cnblogs.com/mgen/archive/2012/05/27/2519730.html

二、例子说明:
本例只是简单地说明如何实现上面的要求,当出现null时,还可以进一步访问下一个层次的数据(当然,返回的结果应该为默认值)

 private static void Main(string[] args)
        {
            var st = new Student {Id = 10, Name = "fudan"};
            var paraExpr = Expression.Parameter(st.GetType());
            //访问 st=>st.Name
            var idProp = st.GetType().GetProperty("Id");
            var idExpr = CreatePropertyAccesorExpression(idProp, paraExpr, null); //生成的表达式大意为:"st.Id"
            var idlambda = Expression.Lambda<Func<Student, int>>(idExpr, paraExpr); //生成的表达式大意为:"st=>st.Id"
            Func<Student, int> idFunc = idlambda.Compile(); //生成相应的delegate;
            var idValue = idFunc(st); //执行: st=>st.Id;
            Console.WriteLine("id= "+idValue); //输出
            //现在访问 st=>st.Location.Row ; 由于Location为null,因此,我们期望访问的Row属性默认值为:0
            var localProp = st.GetType().GetProperty("Location");
            var localExpr = CreatePropertyAccesorExpression(localProp, paraExpr, null); //生成的表达式大意为:"st.Location!=null?st.Location:null"
            var rowProp = localProp.PropertyType.GetProperty("Row");
            /*生成的表达式大意为:
             * var parentExpr = st.Location!=null?st.Location:null;
             * if(parentExpr!=null){
             * return parentExpr.Row;
             * }else{
             * return default(int);
             * }
             */
            var rowExpr = CreatePropertyAccesorExpression(rowProp, localExpr, null);
            //生成lambda: st=>(rowExpr表达式块)
            var rowlambda = Expression.Lambda<Func<Student, int>>(rowExpr, paraExpr);
            Func<Student, int> rowFunc = rowlambda.Compile();//生成相应的delegate
            var rowValue = rowFunc(st); //执行: st=>(rowExpr表达式块)
            Console.WriteLine("rowValue= " + rowValue);
        }

//生成相应的访问表达式块
        static BlockExpression CreatePropertyAccesorExpression(PropertyInfo prop, Expression parentExpr,
           object defaultValue)
        {
            //默认值
            if (defaultValue == null && prop.PropertyType.IsValueType)
            {
                defaultValue = Activator.CreateInstance(prop.PropertyType);
            }
            //默认返回Expression
            var defaultExpr = Expression.Constant(defaultValue, prop.PropertyType);
            //测试当前属性的“父属性”是否非空
            var test = Expression.NotEqual(parentExpr, Expression.Constant(null));
            //“父属性”非空时的返回值
            var propExpr = Expression.Property(parentExpr, prop);
            var labelTarget = Expression.Label(prop.PropertyType);
            //局部变量,用于存放最终的返回值
            var locExpr = Expression.Variable(prop.PropertyType);
            //测试并赋值
            var ifesleTestExpr = Expression.IfThenElse(test, Expression.Assign(locExpr, propExpr),
                Expression.Assign(locExpr, defaultExpr));
            //返回值
            var retExpr = Expression.Return(labelTarget, locExpr);
            var labelExpr = Expression.Label(labelTarget, locExpr);
            var block = Expression.Block(
                new[] { locExpr },
                ifesleTestExpr,
                retExpr,
                labelExpr
                );
            return block;
        }

执行结果:
id= 10;
rowValue= 0;
————————————————
版权声明:本文为CSDN博主「kmguo」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kmguo/article/details/20376187

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部
返回顶部
顶部