电竞比分网-中国电竞赛事及体育赛事平台

分享

貨!表達(dá)式樹解析"框架"(1)

 昵稱10504424 2014-01-17

關(guān)于我和表達(dá)式樹

  其實(shí)我也沒有深入了解表達(dá)式樹一些內(nèi)在實(shí)現(xiàn)的原理,所以具體來說它到底是個(gè)什么東西我也不是很清楚,我的理解只是他是可以將'部分'委托構(gòu)造成對(duì)象,方便我們對(duì)他進(jìn)行解析; 雖然我沒有完全搞懂表達(dá)式樹,但這并不妨礙我使用它(如果以后有時(shí)間,我想我會(huì)更深入的去和它接觸一下)

Lamda + 表達(dá)式樹的性能瓶頸

  對(duì)于Lamda表達(dá)式樹我的感覺是又愛又恨

  書寫和閱讀方面,無疑是非常成功的,他大大的減少了書寫難度,增加了可讀性

  但另一方面,在程序的性能上又是如此的糟糕

  來看下面一個(gè)栗子:

復(fù)制代碼
static void Main()
{
    Where1(u => u.Name == "1");
    Where2(u => u.Name == "1");
}

public static void Where1(Expression<Func<User, bool>> expr)
{

}

public static void Where2(Func<User, bool> expr)
{

}
復(fù)制代碼

  栗子中的 Where1 Where2 兩個(gè)方法,唯一的不同,一個(gè)是委托,一個(gè)是表達(dá)式樹

  同樣運(yùn)行Where1和Where2 一萬次,Where2是0ms,Where1是57ms

  也就是說從Func<User, bool>轉(zhuǎn)為Expression<Func<User, bool>>一萬次需要57ms

  這對(duì)于我這樣一個(gè)追求性能的人來說,實(shí)在是有點(diǎn)難受!

  到不至于不能接受,只有有點(diǎn)難受

  但從另一方面我也在考慮,為什么像這樣的lamda不能直接預(yù)編譯成Expression呢?期待微軟的優(yōu)化吧~

偽框架

  為什么我的標(biāo)題中的框架為帶有引號(hào)?

  因?yàn)槲矣X得這其實(shí)是一個(gè)偽框架

  但他確實(shí)能幫助我們更好的解析Expression對(duì)象,或許應(yīng)該把他稱之為解決方案或是別的

  不過~管他呢!能用就行了

你應(yīng)該了解的Expression

  剛才說了雖然我也沒有完全搞懂,但是有些東西還是應(yīng)該知道的
比如:

  • 以Expression作為基類的子類一共有多少個(gè)
  • 他們分別是干什么的

  第一個(gè)問題比較簡單

  1. 現(xiàn)在在vs中敲下 System.Linq.Expressions.Expression
  2. 然后按F1
  3. 如果順利的話,你現(xiàn)在已經(jīng)打開了"MSDN"
  4. 如果沒有的話就手動(dòng)點(diǎn)一下吧
  5. 然后滾動(dòng)到屏幕最下面

  

  好了這里看到的就是所有`public`的子類(其實(shí)沒有公開的還有更多)

  至于他們分別是干什么用的,每一個(gè)都有自己不同的用法,這里不可能一一說明了,下面的內(nèi)容也只會(huì)涉及到一部分,其他就要自己慢慢研究了

  舉個(gè)栗子:

BinaryExpression ----表示包含二元運(yùn)算符的表達(dá)式

最基礎(chǔ)的用法就是它的三個(gè)屬性Left ,Right ,NodeType
Left 獲取二元運(yùn)算的左操作數(shù)。
Right 獲取二元運(yùn)算的右操作數(shù)。
NodeType 獲取此 Expression 的節(jié)點(diǎn)類型。
it = it.Name == "blqw"就是一個(gè)BinaryExpression
Left = it.Name
Right = "blqw"
NodeType = Equals

  大概就是這么一個(gè)意思吧

效果預(yù)覽

框架結(jié)構(gòu)

  嗯.允許我還是叫他框架吧,畢竟聽上去比較高端大氣上檔次啊

  暫時(shí)是這樣

  • Parsers文件夾里面的是具體對(duì)應(yīng)每一種表達(dá)式樹的解析的具體實(shí)現(xiàn)
  • ExpressionParser     表達(dá)式樹解析器抽象基類,實(shí)現(xiàn)IExpressionParser
  • ExpressionTypeCode   枚舉,枚舉了所有的類型的表達(dá)式樹
  • IExpressionParser    表達(dá)式樹解析器接口
  • Parser          調(diào)用解析器的靜態(tài)對(duì)象,也可以看作入口或工廠,根據(jù)表達(dá)式樹類型調(diào)用具體類
  • ParserArgs        解析器參數(shù),在解析表達(dá)式樹的方法中保持傳遞,可以保存解析中所使用的參數(shù)和保存解析結(jié)果

代碼

復(fù)制代碼
    public class ParserArgs
    {
        public ParserArgs()
        {
            Builder = new StringBuilder();
        }

        public StringBuilder Builder { get; private set; }

        public IList<ParameterExpression> Forms { get; set; }

        public readonly string[] FormsAlias = { "it", "A", "B", "C", "D", "E" };
    }
復(fù)制代碼
IExpressionParser
復(fù)制代碼
    /// <summary> 表達(dá)式樹解析接口
    /// </summary>
    public interface IExpressionParser
    {
        void Select(Expression expr, ParserArgs args);
        void Where(Expression expr, ParserArgs args);
        void GroupBy(Expression expr, ParserArgs args);
        void Having(Expression expr, ParserArgs args);
        void OrderBy(Expression expr, ParserArgs args);
        void Object(Expression expr, ParserArgs args);
    }
復(fù)制代碼
復(fù)制代碼
    /// <summary> 表達(dá)式樹解析抽象泛型類
    /// </summary>
    public abstract class ExpressionParser<T> : IExpressionParser
        where T : Expression
    {
        public abstract void Select(T expr, ParserArgs args);
        public abstract void Where(T expr, ParserArgs args);
        public abstract void GroupBy(T expr, ParserArgs args);
        public abstract void Having(T expr, ParserArgs args);
        public abstract void OrderBy(T expr, ParserArgs args);
        public abstract void Object(T expr, ParserArgs args);

        public void Select(Expression expr, ParserArgs args)
        {
            Select((T)expr, args);
        }

        public void Where(Expression expr, ParserArgs args)
        {
            Where((T)expr, args);
        }

        public void GroupBy(Expression expr, ParserArgs args)
        {
            GroupBy((T)expr, args);
        }

        public void Having(Expression expr, ParserArgs args)
        {
            Having((T)expr, args);
        }

        public void OrderBy(Expression expr, ParserArgs args)
        {
            OrderBy((T)expr, args);
        }

        public void Object(Expression expr, ParserArgs args)
        {
            Object((T)expr, args);
        }
    }
復(fù)制代碼
復(fù)制代碼
    /// <summary> 表達(dá)式類型枚舉
    /// </summary>
    public enum ExpressionTypeCode
    {
        /// <summary> 未知類型表達(dá)式
        /// </summary>
        Unknown = 0,
        /// <summary> 空表達(dá)式 null
        /// </summary>
        Null = 1,
        /// <summary> 表示包含二元運(yùn)算符的表達(dá)式。
        /// </summary>
        BinaryExpression = 2,
        /// <summary> 表示一個(gè)包含可在其中定義變量的表達(dá)式序列的塊。
        /// </summary>
        BlockExpression = 3,
        /// <summary> 表示包含條件運(yùn)算符的表達(dá)式。
        /// </summary>
        ConditionalExpression = 4,
        /// <summary> 表示具有常量值的表達(dá)式。
        /// </summary>
        ConstantExpression = 5,
        /// <summary> 發(fā)出或清除調(diào)試信息的序列點(diǎn)。 這允許調(diào)試器在調(diào)試時(shí)突出顯示正確的源代碼。
        /// </summary>
        DebugInfoExpression = 6,
        /// <summary> 表示類型或空表達(dá)式的默認(rèn)值。
        /// </summary>
        DefaultExpression = 7,
        /// <summary> 表示動(dòng)態(tài)操作。
        /// </summary>
        DynamicExpression = 8,
        /// <summary> 表示無條件跳轉(zhuǎn)。 這包括 return 語句、break 和 continue 語句以及其他跳轉(zhuǎn)。
        /// </summary>
        GotoExpression = 9,
        /// <summary> 表示編制屬性或數(shù)組的索引。
        /// </summary>
        IndexExpression = 10,
        /// <summary> 表示將委托或 lambda 表達(dá)式應(yīng)用于參數(shù)表達(dá)式列表的表達(dá)式。
        /// </summary>
        InvocationExpression = 11,
        /// <summary> 表示一個(gè)標(biāo)簽,可以將該標(biāo)簽放置在任何 Expression 上下文中。 
        /// </summary>
        LabelExpression = 12,
        /// <summary> 描述一個(gè) lambda 表達(dá)式。 這將捕獲與 .NET 方法體類似的代碼塊。
        /// </summary>
        LambdaExpression = 13,
        /// <summary> 表示包含集合初始值設(shè)定項(xiàng)的構(gòu)造函數(shù)調(diào)用。
        /// </summary>
        ListInitExpression = 14,
        /// <summary> 表示無限循環(huán)。 可以使用“break”退出它。
        /// </summary>
        LoopExpression = 15,
        /// <summary> 表示訪問字段或?qū)傩浴?        /// </summary>
        MemberExpression = 16,
        /// <summary> 表示調(diào)用構(gòu)造函數(shù)并初始化新對(duì)象的一個(gè)或多個(gè)成員。
        /// </summary>
        MemberInitExpression = 17,
        /// <summary> 表示對(duì)靜態(tài)方法或?qū)嵗椒ǖ恼{(diào)用。
        /// </summary>
        MethodCallExpression = 18,
        /// <summary> 表示創(chuàng)建新數(shù)組并可能初始化該新數(shù)組的元素。
        /// </summary>
        NewArrayExpression = 19,
        /// <summary> 表示構(gòu)造函數(shù)調(diào)用。
        /// </summary>
        NewExpression = 20,
        /// <summary> 表示命名的參數(shù)表達(dá)式。
        /// </summary>
        ParameterExpression = 21,
        /// <summary> 一個(gè)為變量提供運(yùn)行時(shí)讀/寫權(quán)限的表達(dá)式。
        /// </summary>
        RuntimeVariablesExpression = 22,
        /// <summary> 表示一個(gè)控制表達(dá)式,該表達(dá)式通過將控制傳遞到 SwitchCase 來處理多重選擇。
        /// </summary>
        SwitchExpression = 23,
        /// <summary> 表示 try/catch/finally/fault 塊。
        /// </summary>
        TryExpression = 24,
        /// <summary> 表示表達(dá)式和類型之間的操作。
        /// </summary>
        TypeBinaryExpression = 25,
        /// <summary> 表示包含一元運(yùn)算符的表達(dá)式。
        /// </summary>
        UnaryExpression = 26,
    }
復(fù)制代碼
復(fù)制代碼
    /// <summary> 解析器靜態(tài)對(duì)象 
    /// </summary>
    public static class Parser
    {
        private static readonly IExpressionParser[] Parsers = InitParsers();

        static IExpressionParser[] InitParsers()
        {
            var codes = Enum.GetValues(typeof(ExpressionTypeCode));
            var parsers = new IExpressionParser[codes.Length];

            foreach (ExpressionTypeCode code in codes)
            {
                if (code.ToString().EndsWith("Expression"))
                {
                    var type = Type.GetType(typeof(Parser).Namespace + code.ToString() + "Parser");
                    if (type != null)
                    {
                        parsers[(int)code] = (IExpressionParser)Activator.CreateInstance(type);
                    }
                }
            }
            return parsers;
        }

        /// <summary> 得到表達(dá)式類型的枚舉對(duì)象 </summary>
        /// <param name="expr"> 擴(kuò)展對(duì)象:Expression </param>
        /// <returns> </returns>
        public static ExpressionTypeCode GetCodeType(Expression expr)
        {
            if (expr == null)
            {
                return ExpressionTypeCode.Null;
            }
            if (expr is BinaryExpression)
            {
                return ExpressionTypeCode.BinaryExpression;
            }
            if (expr is BlockExpression)
            {
                return ExpressionTypeCode.BlockExpression;
            }
            if (expr is ConditionalExpression)
            {
                return ExpressionTypeCode.ConditionalExpression;
            }
            if (expr is ConstantExpression)
            {
                return ExpressionTypeCode.ConstantExpression;
            }
            if (expr is DebugInfoExpression)
            {
                return ExpressionTypeCode.DebugInfoExpression;
            }
            if (expr is DefaultExpression)
            {
                return ExpressionTypeCode.DefaultExpression;
            }
            if (expr is DynamicExpression)
            {
                return ExpressionTypeCode.DynamicExpression;
            }
            if (expr is GotoExpression)
            {
                return ExpressionTypeCode.GotoExpression;
            }
            if (expr is IndexExpression)
            {
                return ExpressionTypeCode.IndexExpression;
            }
            if (expr is InvocationExpression)
            {
                return ExpressionTypeCode.InvocationExpression;
            }
            if (expr is LabelExpression)
            {
                return ExpressionTypeCode.LabelExpression;
            }
            if (expr is LambdaExpression)
            {
                return ExpressionTypeCode.LambdaExpression;
            }
            if (expr is ListInitExpression)
            {
                return ExpressionTypeCode.ListInitExpression;
            }
            if (expr is LoopExpression)
            {
                return ExpressionTypeCode.LoopExpression;
            }
            if (expr is MemberExpression)
            {
                return ExpressionTypeCode.MemberExpression;
            }
            if (expr is MemberInitExpression)
            {
                return ExpressionTypeCode.MemberInitExpression;
            }
            if (expr is MethodCallExpression)
            {
                return ExpressionTypeCode.MethodCallExpression;
            }
            if (expr is NewArrayExpression)
            {
                return ExpressionTypeCode.NewArrayExpression;
            }
            if (expr is NewExpression)
            {
                return ExpressionTypeCode.NewArrayExpression;
            }
            if (expr is ParameterExpression)
            {
                return ExpressionTypeCode.ParameterExpression;
            }
            if (expr is RuntimeVariablesExpression)
            {
                return ExpressionTypeCode.RuntimeVariablesExpression;
            }
            if (expr is SwitchExpression)
            {
                return ExpressionTypeCode.SwitchExpression;
            }
            if (expr is TryExpression)
            {
                return ExpressionTypeCode.TryExpression;
            }
            if (expr is TypeBinaryExpression)
            {
                return ExpressionTypeCode.TypeBinaryExpression;
            }
            if (expr is UnaryExpression)
            {
                return ExpressionTypeCode.UnaryExpression;
            }
            return ExpressionTypeCode.Unknown;
        }

        /// <summary> 得到當(dāng)前表達(dá)式對(duì)象的解析組件 </summary>
        /// <param name="expr"> 擴(kuò)展對(duì)象:Expression </param>
        /// <returns> </returns>
        public static IExpressionParser GetParser(Expression expr)
        {
            var codetype = GetCodeType(expr);
            var parser = Parsers[(int)codetype];
            if (parser == null)
            {
                switch (codetype)
                {
                    case ExpressionTypeCode.Unknown:
                        throw new ArgumentException("未知的表達(dá)式類型", "expr");
                    case ExpressionTypeCode.Null:
                        throw new ArgumentNullException("expr", "表達(dá)式為空");
                    default:
                        throw new NotImplementedException("尚未實(shí)現(xiàn)" + codetype + "的解析");
                }
            }
            return parser;
        }

        public static void Select(Expression expr, ParserArgs args)
        {
            GetParser(expr).Select(expr, args);
        }

        public static void Where(Expression expr, ParserArgs args)
        {
            GetParser(expr).Where(expr, args);
        }

        public static void GroupBy(Expression expr, ParserArgs args)
        {
            GetParser(expr).GroupBy(expr, args);
        }

        public static void Having(Expression expr, ParserArgs args)
        {
            GetParser(expr).Having(expr, args);
        }

        public static void OrderBy(Expression expr, ParserArgs args)
        {
            GetParser(expr).OrderBy(expr, args);
        }

        public static void Object(Expression expr, ParserArgs args)
        {
            GetParser(expr).Object(expr, args);
        }

    }
復(fù)制代碼

原理分解

首先將所有類型的表達(dá)式樹以枚舉的形式表現(xiàn)出來,1來是為了更直觀便于2是為了給他們編號(hào)

有了編號(hào)就可以方便的在數(shù)組或集合中給他們安排位置

初始化

在Parser類中,放置一個(gè)靜態(tài)字段

private static readonly IExpressionParser[] Parsers = InitParsers();

在InitParsers方法中,使用反射查找當(dāng)前命名空間下名稱為 枚舉名 + Parser 的類,如果有則實(shí)例化,并根據(jù)枚舉的值,在集合中保存

ps:枚舉名 + Parser 作為解析器的命名規(guī)則,僅僅是為了方便反射調(diào)用,Parsers[0] = new xxx() 這個(gè)依然是可以由后期調(diào)用綁定的

調(diào)用

然后提供一個(gè)方法,用于獲取當(dāng)前表達(dá)式對(duì)象對(duì)應(yīng)的枚舉值

復(fù)制代碼
        public static ExpressionTypeCode GetCodeType(Expression expr)
        {
            if (expr == null)
            {
                return ExpressionTypeCode.Null;
            }
            if (expr is BinaryExpression)
            {
                return ExpressionTypeCode.BinaryExpression;
            }
            if (expr is BlockExpression)
            {
                return ExpressionTypeCode.BlockExpression;
            }
            ...
            ...
            return ExpressionTypeCode.Unknown;
        }
復(fù)制代碼

這里的方法我沒有選擇用反射來獲取枚舉值,還是基于對(duì)性能的考慮,這樣測試快5~10倍,有興趣的可以測試一下

復(fù)制代碼
        public static ExpressionTypeCode GetCodeType(Expression expr)
        {
            if (expr == null)
            {
                return ExpressionTypeCode.Null;
            }
            ExpressionTypeCode tc;
            if (Enum.TryParse(expr.GetType().Name, out tc))
            {
                return tc;
            }
            return ExpressionTypeCode.Unknown;
        }
復(fù)制代碼

得到枚舉之后,就可以按枚舉的值,從集合中獲取已經(jīng)實(shí)例化的解析器為了方便調(diào)用,寫了一個(gè)方法GetParser

復(fù)制代碼
public static IExpressionParser GetParser(Expression expr)
{
    var codetype = GetCodeType(expr);
    var parser = Parsers[(int)codetype];
    if (parser == null)
    {
        switch (codetype)
        {
            case ExpressionTypeCode.Unknown:
                throw new ArgumentException("未知的表達(dá)式類型", "expr");
            case ExpressionTypeCode.Null:
                throw new ArgumentNullException("expr", "表達(dá)式為空");
            default:
                throw new NotImplementedException("尚未實(shí)現(xiàn)" + codetype + "的解析");
        }
    }
    return parser;
}
復(fù)制代碼



    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多