Util應用程序框架公共操做類(十二):Lambda表達式公共操做類(三)

  今天在開發一個簡單查詢時,發現個人Lambda操做類的GetValue方法沒法正確獲取枚舉類型值,以致查詢結果錯誤。html

  我增長了幾個單元測試來捕獲錯誤,代碼以下。express

     /// <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的代碼要少,但因爲使用表達式的方式千差萬別,負擔愈來愈重,以致沒法維護了。spa

  爲了完全重構GetValue方法,我須要補充一點表達式解析的知識,我打開開源框架linq2db,仔細觀察他是如何解析的。終於看出點眉目,依靠NodeType進行遞歸判斷。調試

  我之前只知道使用NodeType進行判斷,但不知道應該採用遞歸的方式,真是知其然不知其因此然。code

  我對GetValue進行了重構,代碼以下。htm

     /// <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的方式,可以一次修復,永絕後患,值得你擁有。blog

  同時,我還重構了其它相似的代碼,就再也不貼出,下次我發放源碼時,有興趣能夠看看。

   .Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。

  謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/

相關文章
相關標籤/搜索