习惯了用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.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 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;
}
}
}
随便调用试试:
{
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;
}
呵呵,当一切语法糖条件具备,看吧,就那几行代码,感谢万能的编译器。感谢聪明的设计师。