习惯了用LINQ的人一定用的很爽, 整天LINQ来LINQ去的, LINQ确实是很好的东西, 简洁, 方便, 最重要的是统一了数据的查询方式, 使得程序员不必关心数据的出处(绝对不关心数据的出处是不可能的, 但至少统一了查询方式, 统一了"项目接口"), 这一点对软件工程很有意义.
不过...........它是如何实现的呢?...................
恩, 又是语法糖, 不这回是个超级大语法糖, 外带一些类库糖.
如果把整个LINQ比作一个人,那我们先来写一个麻雀:
//
定义委托类型。
public
delegate
T TheFun
<
T
>
();
//
定义一个接口。
public
interface
IHaveCount
{
int
MyCount1 {
get
;
set
; }
int
MyCount2 {
get
;
set
; }
}
//
这是类库糖
public
static
class
HaveCountOperations
{
//
使用扩展方法这个语法糖。
public
static
IHaveCount AddCount2ToCount1(
this
IHaveCount haveCount, TheFun
<
bool
>
ifDoOrNot)
{
//
执行委托,用来判断是否执行。
if
(ifDoOrNot.Invoke())
{
haveCount.MyCount1
+=
haveCount.MyCount2;
}
return
haveCount;
}
}
//
定义一个类
public
class
A : IHaveCount
{
public
string
Name;
#region
IHaveCount Members
public
int
MyCount1 {
get
;
set
; }
public
int
MyCount2 {
get
;
set
; }
#endregion
}
然后调用它:
A a
=
new
A { MyCount1
=
100
, MyCount2
=
2
};
a.AddCount2ToCount1(()
=>
true
).AddCount2ToCount1(()
=>
true
).AddCount2ToCount1(()
=>
false
);
Console.WriteLine(a.MyCount1.ToString());
结果为104。
我们回过头来看一下,不难发现在这个麻雀结构中最关键的就是类库糖中针对接口IHaveCount的扩展方法,麻雀虽小,但很能反映问题。LINQ中,最本质的也是这样一种设计思路。伟大的LINQ设计师当初就要面对这样一种场景:如何针对不同的载体类型,统一数据集的查询方式?
绝不可能去重写所有的载体类型比如List、Array,但这些集合类型都实现了同样的一个接口:IEnumerable<T>,也就是说都是可以使用迭代器查看数据集中的每一个数据的,只要能把查询条件以某种方式传到foreach结构中,然后再返回符合条件的同样继承IEnumerable<T>的集合不就可以设计出类似sql的查询语言?查询条件可以使用委托,下一个问题是如何返回集合,当然可以新new一个,不过聪明的LINQ设计师却使用了yield return,呵,这样一来,不仅减少了代码量,还顺便实现了延迟加载的特性。这样一来,核心设计便没有问题了,可是使用起来却比较难看,于是有了扩展方法,让类库糖调用起来像实例方法一样方便,于是又产生了匿名委托,匿名方法等等等等,可以说C# 3.0绝大部分新特性都被用在了LINQ上。再结合泛型方法,泛型委托,LINQ看起来竟显得如此漂亮。
如果把整个LINQ比作一个人,那我们最后再来写一条狗:(编译完了强烈建议大家用Reflector反编译仔细研究研究)
public
static
class
LINQDogOperations
{
public
static
IEnumerable
<
TResult
>
LittleDogSelect
<
TSource, TResult
>
(
this
IEnumerable
<
TSource
>
source, Func
<
TSource, TResult
>
selector)
{
foreach
(TSource ts
in
source)
{
yield
return
selector(ts);
}
}
public
static
IEnumerable
<
TSource
>
LittleDogWhere
<
TSource
>
(
this
IEnumerable
<
TSource
>
source, Func
<
TSource,
bool
>
predicate)
{
foreach
(TSource ts
in
source)
{
if
(predicate(ts))
{
yield
return
ts;
}
}
}
随便调用试试:
class
Program
{
static
void
Main(
string
[] args)
{
List
<
TestClass
>
test
=
new
List
<
TestClass
>
();
for
(
int
i
=
0
; i
<
1000
; i
++
)
{
test.Add(
new
TestClass { ID
=
i, Password
=
i.ToString() });
}
var v1
=
test.LittleDogSelect((i)
=>
new
{ i.ID, i.Password }).LittleDogWhere((c)
=>
c.ID
>
500
);
Console.WriteLine(v1.Count().ToString());
}
}
public
class
TestClass
{
public
int
ID;
public
string
Password;
public
int
State;
}
呵呵,当一切语法糖条件具备,看吧,就那几行代码,感谢万能的编译器。感谢聪明的设计师。