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的牛人可以完善。
|