Linq本质论

ccoderlh 2008-01-14 04:07:57
本文的目的是帮助大家搞清楚C#3.0里面的Linq查询表达式的来龙去脉,搞清楚以后对C#3.0的新特性基本上也就了然于胸了。建议大家看完Linq查询后再看各个语法新特性对Linq的意义,这样会更加有帮助一些。

1、自动属性。

class Class
{
//C#2.0 属性
//private int _id;
//public int ID
//{
// get {
// return _id;
// }

// set
// {
// _id = value;
// }
//}

//C#3.0 属性 可以给get set加访问修饰符
public int ID { get; private set; }
public string Name { get; set; }
public Class(int id)
{
//加了private之后的属性只能在类的内部访问
this.ID = id;
}
}

本质:和原来的属性没啥两样 ,简化了语法而已。

对Linq的意义:无。


2、初始化器。

private static void Initializer()
{
//C#2.0 对象初始化
//Class c = new Class(1);
//c.Name = "终极一班";
//C#3.0 对象初始化器
Class c = new Class(1) { Name = "终极一班" };
//C#2.0 集合初始化
//ClassCollection list = new ClassCollection();
//list.Add(c);
//C#3.0 集合初始化器
ClassCollection list = new ClassCollection
{
new Class(1) { Name="终极一班"},
new Class(2){Name="终极二班"}
};
foreach (Class item in list)
{
Console.WriteLine(item.ID + " " + item.Name);

}
}

相关的班级集合类代码:

class ClassCollection : List<Class>
{ }

本质:和原来的构造函数初始化或构造后通过属性初始化没啥两样 ,简化了语法而已。

对Linq的意义:和匿名类型结合起来构造查询结果集合里面的新元素类型。


4、具有隐式类型的局部变量

private static void Var()
{
var i = 1;// 编译过后的结果实际是 int i=1; var并不是动态变量,它的类型实际上是c#编译器通过上下文推断是int
//var i = DateTime.Now; //编译不过,和JavaScript不一样
var d = DateTime.Now;//=后面支持各种类型
var a = new int[] { 1, 2, 3 };//var也支持数组
foreach (var item in a)//item的类型通过C#编译器推断得知是int
{
Console.WriteLine(i);
}

//var x; // 错误,没有用来推断类型的初始化器
//var y = { 1, 2, 3 }; // 错误,不允许使用集合初始化器
//var z = null; // 错误,不允许出现空类型
}

本质:var并非动态类型 ,C#仍然是静态语言,引入var方便我们写代码了,可以不管“=”后面的赋值表达式类型了,由编译器自己去推断生成对应类型了。

对Linq的意义:可以自动推断出Linq查询返回的集合类型。



5、匿名类型。

private static void AnonymousType()
{
var v = new { Name = "张三", Sex = true };//无须显示声明一个类,而且在初始化器里面可以获取上下文的变量——闭包
Console.WriteLine(v.Name);
}

本质:有了匿名类型后我们不需要显示的声明一个类型了,这个类型由C#编译器自动生成,而且利用了初始化器和var的新特性

对Linq的意义:和初始化器结合起来构造查询结果集合里面的新元素类型。


6、扩展方法。

比如我们现在想给int类型增加(扩展)一个方法,判断一个整数自身是否偶数,我们期望的语法是这样的:

private static void ExtendMethod()
{
int i = 2;
Console.WriteLine(i.IsEven());
}

注意原来int原来是没有IsEven()这个方法的,要实现这个方法,必须写一个静态类和一个静态方法。

static class MyExtention
{
public static bool IsEven(this int num)//this 表示针对int的实例和索引器的this的含义是一样的,int表示给int这种类型进行扩展
{
return num % 2 == 0;
}

}

本质:编译i.IsEven()的本质是C#编译器生成了了MyExtention.IsEven(i)的代码,实际上仍然没有破坏类型的结构,并不是真的象语法那样平白无故给int增加了一个IsEven()方法,和设计模式里面的Visitor模式动态注入方法还是有区别的。

对Linq的意义:用来对集合类型扩展不同的查询方法。


7、Lambda表达式和Linq查询。

接下来我们通过一个例子来看一下Lambda表达式和Linq查询的关系:我们现在想给ClassCollection增加一个过滤方法,方法的目的是能够过滤返回班级名称为“终极一班”的集合来。

0)首先给MyExtention增加这么一个静态方法:

public static ClassCollection Filter(this ClassCollection classes)
{
var newlist = new ClassCollection();
foreach (var item in classes)
{
if (item.Name=="终极一班")

{
newlist.Add(item);
}
}
return newlist;
}

private static void LambdaLinq()
{
var classes = GetClasses();
//var students = GetStudents();
//0 原始版本
var result = classes.Filter();

foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}

}

相关的工厂方法:

static ClassCollection GetClasses()
{
return new ClassCollection{
new Class(1){ Name = "终极一班"},
new Class(2){ Name = "终极二班"},
};
}

1)现在需求发生了变化,需要上面的红色部分需要发生变化,也就是说我们希望这个查询条件可以在我们调用Filter方法的时候动态的指定,这时候我们可以把这个变化封装成一个接口,当然还可以封装成一个委托,这是.net的非常好用的独有特性,委托的最直接的作用可以把一个具体的方法引用封装成一个变量传递。好,开始变形!

delegate bool FilterHandler(Class c); //注意这个要放到namespace下面,不要放到Program类里面

public static ClassCollection Filter(this ClassCollection classes,FilterHandler f)
{
var newlist = new ClassCollection();
foreach (var item in classes)
{
if (f(item))

{
newlist.Add(item);
}
}
return newlist;
}

static bool F(Class c)
{
return c.Name == "终极一班";
}

private static void LambdaLinq()
{
var classes = GetClasses();
// C#1.0 使用委托封装过滤条件
FilterHandler f=new FilterHandler(F);
var result = classes.Filter(f);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}

}

我们声明了一个委托FilterHandler,只要满足这个委托的方法我们都可以传递给Filter方法,这样就实现了动态的改变查询条件的目的,F方法内部可以是任意的查询条件比如return c.Name != "终极一班";同时我们不需要改变Filter方法内部稳定的部分。

2)c#2.0里面也支持直接把一个方法传给一个委托,但本质上也是编译器把方法转换成了一个委托,例如上面:

private static void LambdaLinq()
{
var classes = GetClasses();
// C#2.0 直接传递方法
var result = classes.Filter(F);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}

}

3)C#2.0里面有个新特性,叫匿名方法,我们可以直接传递匿名方法:

private static void LambdaLinq()
{
var classes = GetClasses();
// C#2.0 传递匿名方法
var result = classes.Filter(delegate(Class c) { return c.Name == "终极一班"; });
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}

}

好,变形到这里,我们发现这个匿名其实不仅仅可以给我们带来不用给方法命名的好处,在这个方法内部我们还可以使用外部上下文环境的变量成员,这个特性也叫“闭包(Closure)”,JavaScript也支持这个特性,比如:

private static void LambdaLinq()
{
var classes = GetClasses();

string className = "终极一班";
// C#2.0 传递匿名方法

var result = classes.Filter(delegate(Class c) { return c.Name == className; });
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}

}

...全文
1147 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
Macosx 2008-08-05
  • 打赏
  • 举报
回复
LINQ可不是语法糖 它是真正地提高了程序员的表达能力 LINQ的语法说实话很简单 背后的编程思想可不简单
tomiehu 2008-08-01
  • 打赏
  • 举报
回复
阅~~~~~~~~~
SlaughtChen 2008-07-29
  • 打赏
  • 举报
回复
顶一个
jimodushi 2008-07-16
  • 打赏
  • 举报
回复
学习!
lovingkiss 2008-07-16
  • 打赏
  • 举报
回复
Linq的本质=(自动属性)+(初始化器)+(具有隐式类型的局部变量)+(匿名类型)+(扩展方法)+(更强的类型自动推断)+(Lambda表达式)+(编译为Lambda表达式树)+(Lambda表达式树)+(Linq to Sql)+(Linq to Object)+(Linq to Xml)+(Linq to 其他扩展)
ms44 2008-06-12
  • 打赏
  • 举报
回复
不错。简单的总结了一下。
levenwood 2008-06-02
  • 打赏
  • 举报
回复
收藏~谢谢
pt1314917 2008-06-02
  • 打赏
  • 举报
回复
不错。。
zsxghost 2008-05-30
  • 打赏
  • 举报
回复
语法糖是什么东西?
Infected_U2 2008-05-30
  • 打赏
  • 举报
回复
被楼主那么一指点 豁然开朗啊
flyin2006 2008-05-26
  • 打赏
  • 举报
回复
天上什么在飞?
AechoJohn 2008-05-16
  • 打赏
  • 举报
回复
mark一下,收藏
guilin_gavin 2008-05-16
  • 打赏
  • 举报
回复
拜读!
job_2006 2008-05-09
  • 打赏
  • 举报
回复
关注,收藏
shuihan20e 2008-05-09
  • 打赏
  • 举报
回复
学习了,正准备学呢
MyLf 2008-05-09
  • 打赏
  • 举报
回复
不错。

Linq还是很方便的。
UltraBejing 2008-04-30
  • 打赏
  • 举报
回复
有点难度哦
yitian130 2008-04-28
  • 打赏
  • 举报
回复
up
liyong11111 2008-04-28
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 vwxyzh 的回复:]
Linq的本质=语法糖(自动属性)+语法糖(初始化器)+语法糖(具有隐式类型的局部变量)+语法糖(匿名类型)+语法糖(扩展方法)+语法糖(更强的类型自动推断)+语法糖(Lambda表达式)+语法糖(编译为Lambda表达式树)+类库(Lambda表达式树)+类库(Linq to Sql)+类库(Linq to Object)+类库(Linq to Xml)+类库(Linq to 其他扩展)
[/Quote]
huanyuan12 2008-04-26
  • 打赏
  • 举报
回复
查收

加载更多回复(15)
C#本质论(第3版) 详细介绍C# 4.0 第1章 c#概述 1.1 hello world 1.2 c#语法基础 1.2.1 c#关键字 1.2.2 类型定义 1.2.3 main 1.2.4 语句和语句分隔符 1.2.5 空白 1.3 使用变量 1.3.1 数据类型 1.3.2 变量的声明 1.3.3 变量的赋值 1.3.4 变量的使用 1.4 控制台输入和输出 1.4.1 从控制台获取输入 1.4.2 将输出写入控制台 1.5 注释 1.6 托管执行和公共语言基础结构 1.7 c#和net版本 .1.8 cil和ildasm 1.9 小结 第2章 数据类型 2.1 基本数值类型 2.1.1 整数类型 2.1.2 浮点类型 2.1.3 decimal类型 2.1.4 字面值 2.2 更多基本类型 2.2.1 布尔类型 2.2.2 字符类型 2.2.3 字符串 2.3 null和void 2.3.1 null 2.3.2 void 2.4 类型的分类 2.4.1 值类型 2.4.2 引用类型 2.5 可空修饰符 2.6 数据类型之间的转换 2.6.1 显式转型 2.6.2 隐式转型 2.6.3 不进行转型的类型转换 2.7 数组 2.7.1 数组的声明 2.7.2 数组的实例化和赋值 2.7.3 数组的使用 2.7.4 字符串作为数组使用 2.7.5 常见错误 2.8 小结 第3章 运算符和控制流 3.1 运算符 3.1.1 一元运算符正和负 3.1.2 二元算术运算符 3.1.3 圆括号运算符 3.1.4 赋值运算符 3.1.5 递增和递减运算符 3.1.6 常量表达式 3.2 流控制概述 3.2.1 if语句 3.2.2 嵌套if 3.3 代码块 3.4 作用域和声明空间 3.5 布尔表达式 3.5.1 关系运算符和相等性运算符 3.5.2 逻辑布尔运算符 3.5.3 逻辑求反运算符 3.5.4 条件运算符 3.5.5 空接合运算符 3.6 按位运算符 3.6.1 移位运算符 3.6.2 按位运算符 3.6.3 按位赋值运算符 3.6.4 按位取反运算符 3.7 控制流语句 3.7.1 whi.1 e和do/while循环 3.7.2 for循环 3.7.3 foreach循环 3.7.4 switch语句 3.8 跳转语句 3.8.1 break语句 3.8.2 continue语句 3.8.3 go to语句 3.9 c#预处理器指令 3.9.1 排除和包含代码 3.9.2 定义预处理器符号 3.9.3 生成错误和警告 3.9.4 关闭警告消息 3.9.5 nowarn:选项 3.9.6 指定行号 3.9.7 可视编辑器提示 3.10 小结 第4章 方法和参数 4.1 方法的调用 4.1.1 命名空间 4.1.2 类型名称 4.1.3 作用域 4.1.4 方法名称 4.1.5 参数 4.1.6 方法返回值 4.1.7 语句与方法调用的比较 4.2 方法的声明 4.2.1 参数声明 4.2.2 方法返回值声明 4.3 uslng指令 4.4 main()的返回值和参数 4.5 参数 4.5.1 值参数 4.5.2 引用参数 4.5.3 输出参数 4.5.4 参数数组 4.6 递归 4.7 方法重载 4.8 可选参数 4.9 用异常实现基本错误处理 4.9.1 捕捉错误 4.9.2 使用throw语句报告错误 4.10 小结 第5章 类 5.1 类的定义和实例化 5.2 实例字段 5.2.1 实例字段的声明 5.2.2 实例字段的访问 5.3 实例方法 5.4 使用this关键字 5.5 访问修饰符 5.6 属性 5.6.1 属性的声明 5.6.2 自动实现的属性 5.6.3 命名规范 5.6.4 提供属性验证 5.6.5 读和只写属性 5.6.6 为取值方法和赋值方法指定访问修饰符 5.6.7 属性作为虚字段使用 5.6.8 属性和方法调用不允许作为ref或out参数值使用 5.7 构造器 5.7.1 构造器的声明 5.7.2 默认构造器 5.7.3 对象初始化器 5.7.4 构造器的重载 5.7.5 使用this调用另一个构造器 5.8 静态成员 5.8.1 静态字段 5.8.2 静态方法 5.8.3 静态构造器 5.8.4 静态属性 5.8.5 静态类 5.9 扩展方法 5.10 封装数据 5.10.1
C#5.0本质论第四版,高清扫描的,对C#5.0技术讲的比较详细,第1章 C#概述 1 1.1 Hello,World 1 1.2 C#语法基础 3 1.2.1 C#关键字 3 1.2.2 标识符 4 1.2.3 类型定义 5 1.2.4 Main 6 1.2.5 语句和语句分隔符 7 1.2.6 空白 8 1.2.7 使用变量 8 1.2.8 数据类型 9 1.2.9 变量的声明 9 1.2.10 变量的赋值 10 1.2.11 变量的使用 11 1.3 控制台输入和输出 11 1.3.1 从控制台获取输入 11 1.3.2 将输出写入控制台 12 1.3.3 注释 14 1.3.4 托管执行和公共语言基础结构 16 1.3.5 C#和.NET版本 17 1.3.6 CIL和ILDASM 18 1.4 小结 20 第2章 数据类型 21 2.1 基本数值类型 21 2.1.1 整数类型 22 2.1.2 浮点类型 23 2.1.3 decimal类型 23 2.1.4 字面值 24 2.2 更多基本类型 27 2.2.1 布尔类型 27 2.2.2 字符类型 27 2.2.3 字符串 29 2.3 null和void 34 2.3.1 null 34 2.3.2 void 35 2.4 类型的分类 37 2.4.1 值类型 37 2.4.2 引用类型 37 2.5 可空修饰符 38 2.6 数据类型之间的转换 39 2.6.1 显式转型 39 2.6.2 隐式转型 41 2.6.3 不使用转型操作符的类型转换 42 2.7 数组 43 2.7.1 数组的声明 44 2.7.2 数组的实例化和赋值 45 2.7.3 数组的使用 48 2.7.4 字符串作为数组使用 52 2.7.5 常见数组错误 53 2.8 小结 55 第3章 操作符和控制流 57 3.1 操作符 57 3.1.1 一元操作符正和负 58 3.1.2 二元算术操作符 58 3.1.3 复合赋值操作符 64 3.1.4 递增和递减操作符 65 3.1.5 常量表达式和常量符号 68 3.2 控制流程概述 69 3.2.1 if语句 70 3.2.2 嵌套if 71 3.3 代码块 73 3.4 代码块、作用域和声明空间 74 3.5 布尔表达式 76 3.5.1 关系操作符和相等性操作符 77 3.5.2 逻辑布尔操作符 77 3.5.3 逻辑求反操作符 78 3.5.4 条件操作符 79 3.5.5 空接合操作符 80 3.6 按位操作符 80 3.6.1 移位操作符 81 3.6.2 按位操作符 82 3.6.3 按位赋值操作符 83 3.6.4 按位取反操作符 84 3.7 控制流语句(续) 84 3.7.1 while和do while循环 84 3.7.2 for循环 86 3.7.3 foreach循环 88 3.7.4 switch语句 90 3.8 跳转语句 92 3.8.1 break语句 92 3.8.2 continue语句 94 3.8.3 goto语句 95 3.9 C#预处理指令 97 3.9.1 排除和包含代码 98 3.9.2 定义预处理符号 98 3.9.3 生成错误和警告 99 3.9.4 关闭警告消息 99 3.9.5 nowarn:选项 99 3.9.6 指定行号 100 3.9.7 可视编辑器提示 100 3.10 小结 101 第4章 方法和参数 103 4.1 方法的调用 104 4.1.1 命名空间 105 4.1.2 类型名称 106 4.1.3 作用域 107 4.1.4 方法名称 107 4.1.5 形参和实参 107 4.1.6 方法返回值 107 4.1.7 语句与方法调用的比较 108 4.2 方法的声明 108 4.2.1 形式参数声明 109 4.2.2 方法返回类型声明 110 4.3 using指令 111 4.4 Main()的返回值和参数 114 4.5 方法的参数 116 4.5.1 值参数 116 4.5.2 引用参数(ref) 117 4.5.3 输出参数(out) 118 4.5.4 参数数组 120 4.6 递归 122 4.7 方法重载 124 4.8 可选参数 126 4.9 用异常实现基本错误处理 129 4.9.1 捕捉错误 130 4.9.2 使用throw语句报告错误 136 4.10 小结 138 第5章 类 139 5.1 类的定义和实例化 141 5.2 实例字段 144 5.2.1 实例字段的声明 144 5.2.2 实例字段的访问 144 5.3 实例方法 145 5.4 使用this关键字 146 5.5 访问修饰符 151 5.6 属性 153

8,497

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 LINQ
社区管理员
  • LINQ
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧