zoukankan      html  css  js  c++  java
  • Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

      今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误。

      我增加了几个单元测试来捕获错误,代码如下。

         /// <summary>
            /// 测试值为枚举
            /// </summary>
            [TestMethod]
            public void TestGetValue_Enum() {
                var test1 = new Test1();
                test1.NullableEnumValue = LogType.Error;
    
                //属性为枚举,值为枚举
                Expression<Func<Test1, bool>> expression = test => test.EnumValue == LogType.Debug;
                Assert.AreEqual( LogType.Debug.Value(), Lambda.GetValue( expression ) );
    
                //属性为枚举,值为可空枚举
                expression = test => test.EnumValue == test1.NullableEnumValue;
                Assert.AreEqual( LogType.Error, Lambda.GetValue( expression ) );
    
                //属性为可空枚举,值为枚举
                expression = test => test.NullableEnumValue == LogType.Debug;
                Assert.AreEqual( LogType.Debug, Lambda.GetValue( expression ) );
    
                //属性为可空枚举,值为可空枚举
                expression = test => test.NullableEnumValue == test1.NullableEnumValue;
                Assert.AreEqual( LogType.Error, Lambda.GetValue( expression ) );
    
                //属性为可空枚举,值为null
                test1.NullableEnumValue = null;
                expression = test => test.NullableEnumValue == test1.NullableEnumValue;
                Assert.AreEqual( null, Lambda.GetValue( expression ) );
            }

      单元测试成功捕获了Bug,我打开Lambda操作类,准备修改GetValue方法,代码见Util应用程序框架公共操作类(八):Lambda表达式公共操作类(二)

      面对GetValue杂乱无章的代码,我顿时感觉无法下手,必须彻底重构它。

      我之前也看过一些Lambda表达式解析的代码和文章,基本都是使用NodeType来进行判断。我一直没有使用这种方式,是因为NodeType数量庞大,并且多种NodeType可能转换为同一种Expression类型。我当时认为用switch判断NodeType工作量太大,所以直接采用As转换为特定表达式,再判断是否空值。

      我把这种山寨方法称为瞎猫碰到死耗子,主要依靠单元测试来捕获需求,通过断点调试,我可以知道转换为哪种特定表达式。这种方法在前期看上去貌似很有效,比判断NodeType的代码要少,但由于使用表达式的方式千差万别,负担越来越重,以至无法维护了。

      为了彻底重构GetValue方法,我需要补充一点表达式解析的知识,我打开开源框架linq2db,仔细观察他是如何解析的。终于看出点眉目,依靠NodeType进行递归判断。

      我以前只知道使用NodeType进行判断,但不知道应该采用递归的方式,真是知其然不知其所以然。

      我对GetValue进行了重构,代码如下。

         /// <summary>
            /// 获取值,范例:t => t.Name == "A",返回 A
            /// </summary>
            /// <param name="expression">表达式,范例:t => t.Name == "A"</param>
            public static object GetValue( Expression expression ) {
                if ( expression == null )
                    return null;
                switch ( expression.NodeType ) {
                    case ExpressionType.Lambda:
                        return GetValue( ( (LambdaExpression)expression ).Body );
                    case ExpressionType.Convert:
                        return GetValue( ( (UnaryExpression)expression ).Operand );
                    case ExpressionType.Equal:
                    case ExpressionType.NotEqual:
                    case ExpressionType.GreaterThan:
                    case ExpressionType.LessThan:
                    case ExpressionType.GreaterThanOrEqual:
                    case ExpressionType.LessThanOrEqual:
                        return GetValue( ( (BinaryExpression)expression ).Right );
                    case ExpressionType.Call:
                        return GetValue( ( (MethodCallExpression)expression ).Arguments.FirstOrDefault() );
                    case ExpressionType.MemberAccess:
                        return GetMemberValue( (MemberExpression)expression );
                    case ExpressionType.Constant:
                        return GetConstantExpressionValue( expression );
                }
                return null;
            }
    
            /// <summary>
            /// 获取属性表达式的值
            /// </summary>
            private static object GetMemberValue( MemberExpression expression ) {
                if ( expression == null )
                    return null;
                var field = expression.Member as FieldInfo;
                if ( field != null ) {
                    var constValue = GetConstantExpressionValue( expression.Expression );
                    return field.GetValue( constValue );
                }
                var property = expression.Member as PropertyInfo;
                if ( property == null )
                    return null;
                var value = GetMemberValue( expression.Expression as MemberExpression );
                return property.GetValue( value );
            }
    
            /// <summary>
            /// 获取常量表达式的值
            /// </summary>
            private static object GetConstantExpressionValue( Expression expression ) {
                var constantExpression = (ConstantExpression)expression;
                return constantExpression.Value;
            }

      运行了全部测试,全部通过,说明没有影响之前的功能。这正是自动化回归测试的威力,如果没有单元测试,我哪里敢重构这些代码呢。另外,修改Bug采用TDD的方式,能够一次修复,永绝后患,值得你拥有。

      同时,我还重构了其它类似的代码,就不再贴出,下次我发放源码时,有兴趣可以看看。

       .Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

      谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

  • 相关阅读:
    HDU 1513 最长子序列
    HDU 3033 分组背包变形(每种至少一个)
    HDU 1712 分组背包
    深度学习
    《将博客搬至CSDN》
    UVa 10917 Dijkstra
    hdu 3839 Ancient Messages (dfs )
    LA 4255 UVa1423 拓扑排序
    poj 2515 差分序列,排列组合
    UVA 10054 the necklace 欧拉回路
  • 原文地址:https://www.cnblogs.com/xiadao521/p/4245361.html
Copyright © 2011-2022 走看看