一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

LINQ探秘:實現(xiàn)一個自己的LINQ Provider - .NET技術(shù) / C#

 orion360doc 2011-04-27
<ul id="saage"><sup id="saage"></sup></ul>
    C#添加了一項重要的特性,就是 LINQ。

    借助LINQ,我們可以用查詢語法來檢索對象。

    C# code
                            
    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { List<string> list = new List<string>() { "Hello", "World", "ABC", "China" }; (from x in list where x.Length > 4 select x).ToList().ForEach(x => Console.WriteLine(x)); } } }


    這樣一段代碼,可以篩選出字符串列表里面長度大于4的字符。

    但是 LINQ 是怎么工作的呢?

    其實 LINQ就是調(diào)用了 System.Linq 下的一組擴展方法。

    C# code
                            
    (from x in list where x.Length > 4 select x).ToList().ForEach(x => Console.WriteLine(x));


    等價于:
     
    C# code
                            
    list.Where(x => x.Length > 4 select x).Select(x => x).ToList().ForEach(x => Console.WriteLine(x));


    我們注釋掉 using System.Linq;。

    程序無法編譯了。。。

    下面我們用自己的代碼實現(xiàn) Where、Select和ToList:

    添加如下代碼:
    C# code
                            
    static class MyLinq { public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { foreach (var item in source) { if (predicate(item)) yield return item; } } public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) { foreach (var item in source) { yield return selector(item); } } public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { List<TSource> list = new List<TSource>(); foreach (var item in source) list.Add(item); return list; } }


    編譯,成功!

    但是真的是我們的代碼起作用了么?

    我們修改下:
    C# code
                            
    if (predicate(item)) yield return item;

    我們修改成
    C# code
                            
    if (!predicate(item)) yield return item;


    再次運行,我們發(fā)現(xiàn)過濾反過來了!

    趕快改回來。

    上面的內(nèi)容只是熱身一下,下面重點來了:

    如何實現(xiàn)一個自己的LINQ Provider。

    我覺得在討論這個問題之前,有必要說下為什么我們要實現(xiàn)自己的 Provider。其實很簡單:LINQ是基于對象的,有時候我們需要返回來自遠程的數(shù)據(jù),如果返回所有的數(shù)據(jù),再過濾,就浪費了大量的網(wǎng)絡(luò)帶寬。

    事實上 Linq to SQL 就是這樣——如果沒有 Linq To SQL,我們需要從數(shù)據(jù)庫返回所有的數(shù)據(jù),然后本地篩選,聽聽就是多么恐怖的事?。?!
    Linq To SQL 的原理是,將 LINQ 表達式轉(zhuǎn)換為 SQL,然后再查詢和返回數(shù)據(jù)。

    我們的例子圍繞這樣一個假想的情況展開:我們需要從人人網(wǎng)上獲取新鮮事,人人提供了API,我們可以返回某個人的新鮮事。

    為了簡化起見,例子沒有真正從renren獲取數(shù)據(jù),而是模擬了這個過程。

    下面是源程序:

    C# code
                            
    using System; using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { (from x in new RenrenFeeds() where x.FeedOwner == "張三" select x).ToList().ForEach(x => Console.WriteLine(x)); } } class RenrenFeed { public string FeedOwner { get; set; } public string FeedContent { get; set; } public string URL { get; set; } public override string ToString() { return FeedContent; } } class RenrenFeeds : IQueryable<RenrenFeed>, IQueryProvider { private Expression _expression = null; private IList<RenrenFeed> _feeds = new List<RenrenFeed>(); //在真實的環(huán)境里面,我們傳入不同的Owner,返回指定的數(shù)據(jù) private IList<RenrenFeed> GetFeeds(string Owner = "") { List<RenrenFeed> list = new List<RenrenFeed> { new RenrenFeed() { FeedOwner = "張三", FeedContent = "張三分享了一張圖片", URL = "http:///photo/zhangsan" }, new RenrenFeed() { FeedOwner = "張三", FeedContent = "張三撰寫了一篇日志:如何使用LINQ", URL = "http:///blog/zhangsan" }, new RenrenFeed() { FeedOwner = "李四", FeedContent = "李四分享了一張圖片", URL = "http:///photo/lisi" }, new RenrenFeed() { FeedOwner = "李四", FeedContent = "李四撰寫了一篇日志:C#學(xué)習(xí)筆記", URL = "http:///blog/lisi" } }; if (Owner != "") return list.Where(x => x.FeedOwner == Owner).ToList(); else return list; } public IEnumerator<RenrenFeed> GetEnumerator() { return (this as IQueryable).Provider.Execute<IEnumerator<RenrenFeed>>(_expression); } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator<RenrenFeed>)(this as IQueryable).GetEnumerator(); } public Type ElementType { get { return typeof(RenrenFeed); } } public Expression Expression { get { return Expression.Constant(this); } } public IQueryProvider Provider { get { return this; } } public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { if (typeof(TElement) != typeof(RenrenFeed)) throw new Exception("不能查詢RenrenFeed以外的類型!"); _expression = expression; return (IQueryable<TElement>)this; } public IQueryable CreateQuery(Expression expression) { return (IQueryable<RenrenFeed>)(this as IQueryProvider).CreateQuery<RenrenFeed>(expression); } public TResult Execute<TResult>(Expression expression) { MethodCallExpression methodcall = _expression as MethodCallExpression; //這里沒有完成,我們需要解析條件參數(shù),并且轉(zhuǎn)換成對GetFeeds的正確調(diào)用。 foreach (var param in methodcall.Arguments) { if (param.NodeType == ExpressionType.Quote) { LambdaExpression l = Expression.Lambda(Expression.Convert(param, param.Type)); LambdaExpression l1 = (LambdaExpression)l.Compile().DynamicInvoke(); Func<RenrenFeed, bool> func = (Func<RenrenFeed, bool>)l1.Compile(); _feeds = GetFeeds().Where(x => func(x)).ToList(); } Console.WriteLine(param.ToString()); // 我們可以看到 where 產(chǎn)生的 lambda } return (TResult)_feeds.GetEnumerator(); } public object Execute(Expression expression) { return (this as IQueryProvider).Execute<IEnumerator<RenrenFeed>>(expression); } } }



    很抱歉,這東西實在太復(fù)雜,我只寫了個大概。

    有熟悉表達式API的牛人可以完善。
    #14樓 得分:0回復(fù)于:2011-04-26 08:17:55
    感謝LZ的無私分享


    引用樓主 caozhy 的回復(fù):
    list.Where(x => x.Length > 4 select x).Select(x => x).ToList().ForEach(x => Console.WriteLine(x));

    list.Where(x => x.Length > 4).ToList().ForEach(x => Console.WriteLine(x));
    #17樓 得分:0回復(fù)于:2011-04-26 08:36:52
    表達式樹不是太好理解
    不過做熟悉的話也不會很難
    #22樓 得分:0回復(fù)于:2011-04-26 09:59:26
    這個以前沒見到過誒
    • dongxinxi用戶頭像
    • dongxinxi
    • (人生天地間,忽如遠行客)
    • 等 級:
    #32樓 得分:0回復(fù)于:2011-04-26 10:44:13
    先頂再細看
    • dongxinxi用戶頭像
    • dongxinxi
    • (人生天地間,忽如遠行客)
    • 等 級:
    #35樓 得分:0回復(fù)于:2011-04-26 10:50:27
    那個前天就碰到一個問題,想像SQL那樣動態(tài)構(gòu)造where可選條件,時間關(guān)系臨時用三目應(yīng)赴過去了。
    后來一想,如果條件再復(fù)雜點,三目就力不從心了,或者看著讓人惡心。
    最后才想起之前看到過的動態(tài)構(gòu)造LINQ表達式樹
    謝LZ分享
    #36樓 得分:0回復(fù)于:2011-04-26 10:50:53
    看不懂核心部分
    • caozhy用戶頭像
    • caozhy
    • (小學(xué)畢業(yè),自學(xué)成才)
    • 等 級:
    #37樓 得分:0回復(fù)于:2011-04-26 11:11:17
    引用 35 樓 dongxinxi 的回復(fù):
    那個前天就碰到一個問題,想像SQL那樣動態(tài)構(gòu)造where可選條件,時間關(guān)系臨時用三目應(yīng)赴過去了。
    后來一想,如果條件再復(fù)雜點,三目就力不從心了,或者看著讓人惡心。
    最后才想起之前看到過的動態(tài)構(gòu)造LINQ表達式樹
    謝LZ分享

    對于表達式API和表達式樹,我沒有什么研究。覺得還是晦澀了一些,而且下一個版本的C#會簡化動態(tài)編程模型的。
    #44樓 得分:0回復(fù)于:2011-04-26 11:59:46
    引用 35 樓 dongxinxi 的回復(fù):

    那個前天就碰到一個問題,想像SQL那樣動態(tài)構(gòu)造where可選條件,時間關(guān)系臨時用三目應(yīng)赴過去了。
    后來一想,如果條件再復(fù)雜點,三目就力不從心了,或者看著讓人惡心。
    最后才想起之前看到過的動態(tài)構(gòu)造LINQ表達式樹
    謝LZ分享

    還有個解決方案:
    當(dāng)邏輯判斷比較復(fù)雜時 可抽出來寫成擴展方法
    • dongxinxi用戶頭像
    • dongxinxi
    • (人生天地間,忽如遠行客)
    • 等 級:
    #47樓 得分:0回復(fù)于:2011-04-26 12:03:58
    引用 44 樓 q107770540 的回復(fù):
    引用 35 樓 dongxinxi 的回復(fù):

    那個前天就碰到一個問題,想像SQL那樣動態(tài)構(gòu)造where可選條件,時間關(guān)系臨時用三目應(yīng)赴過去了。
    后來一想,如果條件再復(fù)雜點,三目就力不從心了,或者看著讓人惡心。
    最后才想起之前看到過的動態(tài)構(gòu)造LINQ表達式樹
    謝LZ分享

    還有個解決方案:
    當(dāng)邏輯判斷比較復(fù)雜時 可抽出來寫成擴展方法

    的確是個好辦法
    引用 37 樓 caozhy 的回復(fù):
    對于表達式API和表達式樹,我沒有什么研究。覺得還是晦澀了一些,而且下一個版本的C#會簡化動態(tài)編程模型的。

    太過謙虛了,記得在另外一篇帖子里L(fēng)Z提到的DSL領(lǐng)域語言,需求完了程序就隨之出來了,的確很難想象。
    CLR已經(jīng)加入了越來越多動態(tài)語言特性,程序語法對需求邏輯的實現(xiàn)越來越像自然語言(雖然動態(tài)會損失那么點點性能)
    #58樓 得分:0回復(fù)于:2011-04-27 10:10:18
    復(fù)雜的做成擴展方法是好主意。個人覺得Linq to Sql將表達式轉(zhuǎn)成sql真的是太復(fù)雜了,畢竟linq to XML linq to json這種轉(zhuǎn)換都容易的多
    • asdf311用戶頭像
    • asdf311
    • (dark wanderer)
    • 等 級:
    #61樓 得分:0回復(fù)于:2011-04-27 16:27:19
    感謝LZ的分享,不錯的思路哈,很有啟發(fā)

    C# code
                            
    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { if (typeof(TElement) != typeof(RenrenFeed)) throw new Exception("不能查詢RenrenFeed以外的類型!"); _expression = expression; return (IQueryable<TElement>)this; } 可修改為: public IQueryable<TElement> CreateQuery<TElement>(Expression expression) where TElement : RenrenFeed { _expression = expression; return this as IQueryable<TElement>; }


    可以直接使用泛型的where約束,這樣直接就是編譯錯誤
    還有,個人認為,這里還是用as好點,RenrenFeed應(yīng)該不會涉及用戶自定義類型轉(zhuǎn)換

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

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      国产91人妻精品一区二区三区 | 白丝美女被插入视频在线观看| 日本最新不卡免费一区二区| 91蜜臀精品一区二区三区| 日韩性生活片免费观看| 最好看的人妻中文字幕| 日韩一区二区免费在线观看| 欧美黑人在线一区二区| 在线欧美精品二区三区| 亚洲午夜av一区二区| 中文字幕高清免费日韩视频| 亚洲精品有码中文字幕在线观看 | 久久大香蕉精品在线观看| 中文字幕一区久久综合| 欧美激情床戏一区二区三| 成人日韩在线播放视频| 日韩蜜桃一区二区三区| 日本成人三级在线播放| 久久国产成人精品国产成人亚洲| 日韩美成人免费在线视频| 亚洲综合一区二区三区在线| 久久大香蕉一区二区三区| 久久精品亚洲欧美日韩| 国产又黄又猛又粗又爽的片| 国产精品亚洲一级av第二区| 色婷婷丁香激情五月天| 欧美日韩综合免费视频| 大香蕉网国产在线观看av| 日本中文字幕在线精品| 日本一本在线免费福利| 欧美乱视频一区二区三区| 成人午夜视频精品一区| 午夜福利视频日本一区| 国内胖女人做爰视频有没有| 九九热这里只有免费精品| 国产乱久久亚洲国产精品| 亚洲国产综合久久天堂| 色婷婷亚洲精品综合网| 国产成人人人97超碰熟女| 深夜少妇一区二区三区| 欧美一区二区不卡专区|
      <fieldset id="saage"><menu id="saage"></menu></fieldset>