LINQ体验(2)——C# 3.0新语言特性和改进(上篇)

在第一篇中,知道了Visual Studio 2008新特性,从这篇开始进入此系列的第二部分——介绍C# 3.0新语言特性和改进。

总体来说,Visual Studio 2008和.NET 3.5是建立在.NET2.0核心的基础之上,.NET2.0核心本身将不再变化(如果不了解.NET2.0的朋友,请参看MSDN或者一些经典的书籍),C# 3.0新语言特性在.NET2.0基础上进行了改进,这些改进的功能可以大大简化我们编写程序。关于C# 3.0新语言特性在博客园里的很多朋友都介绍了,我在这里简单介绍一下,记录自己所学的东西,也为后面的LINQ打下基础。

C# 3.0新语言特性和改进包括:

  • 自动属性(Auto-Implemented Properties)
  • 隐含类型局部变量(Local Variable Type Inference)
  • 匿名类型(Anonymous Types)
  • 对象与集合初始化器(Object and Collection Initializers)
  • 扩展方法(Extension Methods)
  • Lambda表达式和Lambda表达式树 (Lambda Expression and Lambda Expression Trees)

自动属性(Auto-Implemented Properties)

自动属性可以避免原来这样我们手工声明一个私有成员变量以及编写get/set逻辑,在VS2008中可以像下面这样编写一个类,编译器会自动地生成私有变量和默认的get/set 操作。你也可以分别定义get和set的“protected”等访问级别。

在.Net2.0框架下,我们可以这样写一个User类:

public class User
{
    private int _id;
    private string _name;
    private int _age;
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

现在,可以这样简化:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

像上面这样的空的get/set属性的话,它会自动为你在类中生成一个私有成员变量,对这个变量实现一个公开的getter 和setter。我们可以使用.NET开发环境所提供的ildasm.exe(IL代码反汇编器)工具来分析程序集或者模块的内容。我就不贴图了。

隐含类型局部变量(Local Variable Type Inference)

C#3.0引进了var这个新关键字,在声明局部变量时可用于替代原先的类型名,即当一个变量声明标识为var类型并且该范围域中没有var名称类型存在,那么这个声明就称为隐含类型局部变量。如下(等同于//后面的显式声明):

var i = 5;//int
var j = 23.56;//double
var k = "C Sharp";//string
var x;//错误
var y = null;//错误
var z = { 1, 2, 3 };//错误

在调试状态下,编译器解释如下

隐含类型局部变量调试

隐含类型局部变量要点

  1. var为关键字,可以根据后面的初始化语句自动推断类型,这个类型为强类型。
  2. 初始化语句必须为表达式,不可以为空。且编译时可以推断类型。一旦初始化之后,只可以存储这种类型。
  3. var声明的仅限于局部变量,不可用于字段。亦可以用于for,foreach,using 等语句中。
  4. 数组也可以作为隐含类型。
  5. 初始化语句不能是一个自身的对象或者集合初始化器,但是他可以是包含一个对象或者初始化器的一个new表达式。
  6. 如果局部变量声明包含了多个声明符,其类型必须相同。

匿名类型(Anonymous Types)

匿名类型允许定义行内类型,无须显式定义类型。常和var配合使用来声明匿名类型。

var p1 = new { Id = 1, Name = "YJingLee", Age = 22 };//属性也不需要申明
var p2 = new { Id = 2, Name = "XieQing", Age = 25 };
p1 = p2;//p1,p2结构相同,可以互相赋值
匿名类型调试

在这里编译器会认为p1,p2相当于:

public class SomeType
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

那么数组怎么定义呢?使用"new[]"关键字来声明数组,加上数组的初始值列表。像这样:

var intArray = new[] { 2, 3, 5, 6 };
var strArray = new[] { "Hello", "World" };
var anonymousTypeArray = new[] 
{ 
    new { Name = "YJingLee", Age = 22 }, 
    new { Name = "XieQing", Age = 25 } 
};
var a = intArray[0];
var b = strArray[0];
var c = anonymousTypeArray[1].Name;
匿名数组调试

匿名类型要点

  1. 可以使用new关键字调用匿名初始化器创建一个匿名类型的对象。
  2. 匿名类型直接继承自System. Object。
  3. 匿名类型的成员是编译器根据初始化器推断而来的一些读写属性。

对象与集合初始化器(Object and Collection Initializers)

对象初始化器 (Object Initializers) :

.NET2.0框架中的类型非常依赖于属性。当生成对象实例和使用新的类型时,在.Net2.0时候我们像这样写:

User user = new User();
user.Id = 1;
user.Name = "YJingLee";
user.Age = 22;

在VS2008中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。我们可以这样简化:像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。

User user = new User { Id = 1, Name = "YJingLee", Age = 22 };

又例如,我把二个人加到一个基于泛型的类型为User的List集合中:

List<User> user = new List<User>{
    new User{Id=1,Name="YJingLee",Age=22},
    new User{Id=2,Name="XieQing",Age=25},
};
对象初始化器调试

如果有相同名字和类型的两个对象初始化器将会产生相同的实例,可以相互赋值。例如:

User user = new User { Id = 1, Name = "YJingLee", Age = 22 };
User user2 = new User { Id = 2, Name = "XieQing", Age = 25 };
user = user2; 

除了在初始化类时设置简单的属性值外,对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。例如我们可以在上面定义的User类型同时拥有一个属于Address类型的叫“Address”的属性:

User user = new User
{
    Id = 1,
    Name = "YJingLee",
    Age = 22,
    Address = new Address
    {
        City = "NanJing",
        Zip = 21000
    }
};

集合初始化器(Collection Initializers):

集合初始化器由一系列集合对象组成,用逗号间隔,使用{}封闭。
集合初始化器可以简化把几个对象一起添加到一个集合,编译器会自动为你做集合插入操作。例如我把七个数加到一个基于泛型的类型为int的List集合中

List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };

对象与集合初始化器要点

  1. 对象初始化器实际上利用了编译器对对象中对外可见的字段和属性进行按序赋值。
  2. 对象初始化器允许只给一部分属性赋值,包括internal访问级别
  3. 对象初始化器可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行。
  4. 集合初始化器会对初始化器中的元素进行按序调用ICollection<T>.Add(T)方法。
  5. 注意对象初始化器和集合初始化器中成员的可见性和调用顺序。
  6. 对象与集合初始化器同样是一种编译时技术。

本系列链接:LINQ体验系列文章导航

LINQ推荐资源

LINQ专题:http://kb.cnblogs.com/zt/linq/ 关于LINQ方方面面的入门、进阶、深入的文章。
LINQ小组:http://space.cnblogs.com/group/linq/ 学习中遇到什么问题或者疑问提问的好地方。


作者:李永京YJingLee's Blog
出处:http://lyj.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Tag标签: LINQ
posted @ 2008-01-08 15:41 李永京 阅读(9846) 评论(33)  编辑 收藏 所属分类: LINQ

  回复  引用  查看    
#1楼 2008-01-08 15:57 | 笨笨来学习      
好文章,谢谢.学习中,期待下一篇.难道俺占了沙发?
  回复  引用  查看    
#2楼 [楼主]2008-01-08 16:11 | 李永京      
@笨笨来学习
:-)是传说中的沙发,o(∩_∩)o...
哎!博客园的插入代码功能没有用,自己定义样式排版好累啊!
  回复  引用  查看    
#3楼 2008-01-08 16:52 | 笑疯^_^      
恩,感觉很不错
  回复  引用  查看    
#4楼 [楼主]2008-01-08 16:58 | 李永京      
@笑疯^_^
语法糖的味道~~还有点甜,酸酸的!呵呵
  回复  引用  查看    
#5楼 2008-01-09 10:39 | ColdDog      
希望最后能有一个实例来把所有3.5的东西串起来。
我有时间的话也来尝试一下:)
  回复  引用  查看    
#6楼 [楼主]2008-01-09 12:17 | 李永京      
@ColdDog
没有问题,在第一篇说过的:先依次介绍一下C# 3.0新语言特性和改进,然后从一条一条LINQ语句分析来贯穿LINQ的知识点。最后通过一个实例程序实战Linq to sql带来全新的编程体验。
  回复  引用    
#7楼 2008-01-09 16:39 | 土衣日行 [未注册用户]
不错,特别要赞一下BLOG的排版,写得很清楚,看起来很舒服!
  回复  引用  查看    
#8楼 [楼主]2008-01-09 20:45 | 李永京      
@土衣日行
^_^ 自己在Expression Web 中排版的,然后复制代码~~
  回复  引用  查看    
#9楼 2008-01-16 23:32 | xuqiang      
好文章, 已经收藏备查, 谢谢!
  回复  引用  查看    
#10楼 2008-01-20 15:38 | david_qie      
刚开是接触3.0,有个问题想问题一下,隐藏类型的 var 定义有点像vb 中的dim不用显式申明为强类型,这样的效率是不是要打些折扣。
  回复  引用  查看    
#11楼 [楼主]2008-01-20 15:59 | 李永京      
@david_qie
var为关键字,就是强类型,不是JS里的弱类型,编译器会根据后面的初始化语句自动判断其类型,对效率不会产生影响
  回复  引用  查看    
#12楼 2008-03-12 09:05 | 欧尔      
不错! 通俗易懂啊! 难得!!
  回复  引用    
#13楼 2008-03-19 19:07 | 糖冒鸡屎 [未注册用户]

以在上面定义的User类型同时拥有一个属于Address类型的叫“Address”的属性:

User user = new User
{
Id = 1,
Name = "YJingLee",
Age = 22,
Address = new Address
{
City = "NanJing",
Zip = 21000
}
};

嘿嘿 不懂 帮忙解释下


  回复  引用  查看    
#14楼 [楼主]2008-03-19 19:59 | 李永京      
User类型同时拥有一个属于Address类型的叫“Address”的属性:
有一个User类:Id,Name,Age,Address 成员。有一个Address类:City ,Zip 两个成员,可以这么说User类包含Address类。
  回复  引用  查看    
#15楼 2008-04-22 17:57 | 鹏鹏_Lovely      
--引用--------------------------------------------------
不错! 通俗易懂啊! 难得!!
--------------------------------------------------------
高人,谢谢啦。鞠躬,敬礼。向你致敬。
--------------------------------------------------------

  回复  引用  查看    
#16楼 [楼主]2008-04-22 21:27 | 李永京      
@鹏鹏_Lovely
呵呵,太夸张了啊。
  回复  引用    
#17楼 2008-07-02 17:43 | xiezhi [未注册用户]
我在调试中发现,匿名类型的字段是只读的。
所以,例子中的P1,P2是不是想当于
public class SomeType
{
int id;
public int Id { get{return id;} }
string name;
public string Name { get{return name;} }
int age;
public int Age { get{return age;} }
}
才更准确?
  回复  引用  查看    
#18楼 [楼主]2008-07-11 20:47 | 李永京      
@xiezhi
这样解释有点麻烦,另外写出的代码有点......可以在get/set上加上一些权限标记。
  回复  引用  查看    
#19楼 2008-07-15 12:55 | daodao      
User user3 = new User
{
ID = 3,
Name = "Wang",
Age = 34,
Address = new Address
{
City = "SH",
Zip = 210000
}
};

编译不过,提示
D:\Users\300007467\My Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs(25,17): error CS0117: 'ConsoleApplication1.User' does not contain a definition for 'Address'

  回复  引用  查看    
#20楼 2008-07-16 15:49 | 莫耶      
不错不错
看来我需要学习一下3.5了
  回复  引用  查看    
#21楼 [楼主]2008-07-28 23:18 | 李永京      
@daodao
这个还需要一个Address 类,这个类中有两个字段。
  回复  引用  查看    
#22楼 2008-08-20 11:03 | net1234      
@@数组也可以作为隐含类型。
数组不可以作为隐含类型。

  回复  引用  查看    
#23楼 2008-08-20 11:13 | net1234      
补充一下:
对象初始化器,对象必须有个无参的构造函数
  回复  引用  查看    
#24楼 [楼主]2008-08-30 19:18 | 李永京      
@net1234
谢谢了
  回复  引用  查看    
#25楼 2008-09-27 22:33 | 徐中      
User user = new User
{
Id = 1,
Name = "YJingLee",
Age = 22,
Address = new Address
{
City = "NanJing",
Zip = 21000
}
};
关于这段代码我不是很明白。我也声明了2个类,用了同样的方法,但是是报错说Class1并不包含Class2。我想问一下,是不是在Class1里也要加一个Class2类型的字段,但是我试了也没用。谢谢

  回复  引用  查看    
#26楼 2008-09-27 22:44 | 徐中      
自己解决了,谢谢,你代码应该是写错了,要在User类里加上Address类型的字段,而在User对象new的时候写的是字段名而不是Address类名。
  回复  引用  查看    
#27楼 [楼主]2008-09-27 22:46 | 李永京      
@徐中
Class1并不包含Class2??是类User中定义Address类,可能你类名称错误???

  回复  引用  查看    
#28楼 [楼主]2008-09-27 22:46 | 李永京      
@徐中
呵呵,这下好了~~

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-09-26 20:14 编辑过


相关链接:
 
9月27日开通