C# 3.0 (.NET 3.5, VS2008)

  第三代C#在语法元素基本完备的基础上提供了全新的开发工具和集合数据查询方式,极大的方便了开发。

1. WPF,WCF,WF

  这3个工程类型奠定了新一代.NET开发的客户端模型,通信模型,工作流模型。

  WPF即将取代Winform成为新一代的桌面程序开发工具,控件与代码的超低耦合给开发带来了极大的方便,脱胎于MVC的MVVM模式也展现了极强的生命力。WCF即将融合原来的Web Service的各种提供方式,彻底屏蔽掉各种细节,开启Service服务的篇章。WF似乎使用的比较少,目前不做评论。

  这3个概念每个都可以写上几本书,这里就不出丑了。

2. Lambda表达式

  Lambda 表达式是一种可用于创建委托或表达式树类型的匿名函数。 通过使用 lambda 表达式,可以创建可作为参数传递或作为函数调用值返回的本地函数。 Lambda 表达式主战场是 Linq 查询表达式,这个在下面会提及;由于Lambda表达式是创建委托和表达式树的,所以它在除Linq之外的地方使用也很广泛,使用Lambda表达式可以写出相当优雅的委托代码。

  若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方值。

  左侧的参数列表可以带参数类型,也可以不带。指定参数类型时通常是因为右侧的语句块中使用了该类型的相关方法;当不带参数类型的时候,就由编译器根据上下文去推断,推断成立则没问题,推断不成立则编译错误。左侧的参数列表需要带"()",但是当只有一个无类型的参数时可省略。

  右侧的如果是单个表达式的话可以不带"{}"和";",是语句块的话则要加上。

  你可以将此表达式分配给委托类型:

delegate int del(int i);
static void Main(string[] args)
{
del myDelegate = x => x * x;
int j = myDelegate(); //j = 25
}

  当然也可以用于创建表达式树类型:

using System.Linq.Expressions;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
}
}
}

  表达式树是一种动态创建表达式的工具,表达式树可以编译和运行;使用它可以动态的创建表达式,它的使用场合主要是在LINQ中区创建动态的查询条件表达式。关于表达式树,有兴趣的同学可以参考MSDN:http://msdn.microsoft.com/zh-cn/library/bb397951.aspx

  总的来说,Lambda表达式是基于delegate又高于delegate的东东,青出于蓝而胜于蓝。它更加弱化的约束极大了丰富了表达式的含义,所谓描述越抽象,意义越丰富嘛!难怪抽象派艺术家的作品生命力都很强。在C#中,约束最强的应该就是继承(包括类的继承,接口的实现),它要求子类扩展父类或接口的时候方法时签名要完全匹配,包括返回值类型,函数名,参数类型等;约束次之的是delegate,它不再要求方法名相同了,只要求返回值类型与参数类型相同;到了lambda表达式了,约束更加弱化,连参数与返回值的类型都不要求完全匹配了,难怪有人说如果当初现有lambda表达式的话,就不会有匿名函数的语法了。

3. 对象初始化器和集合初始化器

  直接初始化的时候就可以初始化是很方便的事,这个终于有了,虽说是在LINQ中使用最多,但是在其它场合使用对象初始化器和集合初始化器编程还是显得特别优雅。这个比较简单不多说了,看例子:

// 基本用法:
User user =new User { Id = , Name ="AA", Age = };
//嵌套使用:
User user =new User
{
Id = ,
Name ="AA",
Age = ,
Address = new Address
{
City ="NanJing",
Zip =
}
};
//类似于对象初始化器初始化一个对象,集合初始化器初始化一个集合,
//一句话,有了它你就不用在将元素通过Add逐个添加了:
//基本使用:
List<int> num =new List<int> { , , , , , , };
//结合对象初始化器,我们可以写出如下简洁的代码:
List<User> user =new List<User>{
new User{Id=,Name="AA",Age=},
new User{Id=,Name="BB",Age=},
};

4. 匿名类型

  很多时候,我们需要使用一个临时的对象,按通常的做法,我们要先定义一个类吧,这样才能实例化这个类得到对象,这样实在是太累了;而且往往这样的对象只需要使用在局部的场合,使用以后就不再使用了,例如从数据库中查询出来的临时结果。在3.0中,我们终于不再需要预先定义一个类型了,CLR会提供一种形式让你动态的生成一个无类型的对象。

  匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。

  在看具体的例子之前,先看一个新的关键字:var。这个关键字在Javascript中使用广泛,在C#中使用var声明一个局部变量(只能在函数中使用var定义变量,这种变量官方称之为隐式类型局部变量)时,编译器会自动根据其赋值语句推断这个局部变量的类型。赋值以后,这个变量的类型也就确定而不可以再进行更改。但是这个var关键字最主要的用途是去生成匿名类型,比如表示一个Linq查询的结果。这个结果可能是ObjectQuery<T>或IQueryable<T>类型的对象,也可能是一个简单的实体类型的对象。这时使用var声明这个对象可以节省很多代码书写上的时间。

  看一下匿名类型的使用方式:

var people = new { Name="AA", Age =  };
Console.WriteLine(people.Name);

  需要注意的是,上面创建的people的Name和Age是只读的,不能去修改。

5. 扩展方法

  Linq扩展了原来的IEnumerable得到IQueryable,如何自然的融入原来的集合中却是一个问题,有了这个语法糖,只要符合规定的语法,引入定义扩展方法的namespace,新添加的方法就像是对象原来就有的方法那样方便使用,这样就在不破坏原对象封装性的前提下给该对象添加了新的行为,还是蛮符合面向对象Open-Close原则的。

  看网上的一个例子:

using System;
using System.Text.RegularExpressions; namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
string email = "someone@somewhere.com";
Console.WriteLine(email.IsValidEmailAddress());
}
} public static class Extensions
{
public static bool IsValidEmailAddress(this string s)
{
Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
return regex.IsMatch(s);
}
}
}

  如上代码所示,扩展方法为一静态方法,声明于一个静态类,其参数前加上一个this关键字,参数的类型表示这个扩展方法要对这个类型进行扩展。如上述代码表示其要对字符串类型进行扩展。使用起来也很方便,所有的string类型现在都多了一个叫IsValidEmailAddress的方法。

6. Linq 

  当所有的配角都到齐的时候,主角之一的Linq (Language Integrated Query,语言集成查询)应该要亮相了,它是3.0时代最为强悍的工具,从此查询集合数据有了最为便捷的方式,3.0中上面的许多特性基本上都是为了Linq 服务的。

  Linq主要包含4个组件——Linq to Objects、Linq to XML、Linq to DataSet 和Linq to SQL,这4个组件对应4种查询的对象:内存集合中的对象,XML,SQL和Dataset。在我的经历中,我用的最多的是前三个,所以这里主要就是总结前三种查询。

  在Linq之前查询数据,基本的方式就是循环遍历,按照条件比较,把得到的结果放到临时集合中。Linq彻底简化了这个过程,使用了简单的方式完成了查询。不管查询上述的哪些对象,方式其实还都是一样的:要么使用查询表达式,要么使用扩展方法。

  以查询内存集合中的对象为例,看一下两种使用方式:

using System.Linq;
List<string> collection = new List<string>();
for (int i = ; i < ; i++)
{
collection.Add("A" + i.ToString());
} // 创建查询表达式来获得序号为偶数的元素    
var queryResults = from s in collection
let index = int.Parse(s.Substring())
where index % ==
select s; // 使用扩展方法来获得序号为偶数的元素    
var queryResults1 = collection.Where(item => {
int index = int.Parse(item.Substring());
return index % == ;
});
// 输出查询结果    
foreach (string s in queryResults)
{
Console.WriteLine(s);
}

  查询Xml的方式比较相似,下面来自网上的例子比较了使用原来Class访问Xml的方式,和使用XLinq访问Xml的方式:

using System.Xml;
using System.Xml.Linq; static void Main(string[] args)    
{        
    Console.WriteLine("使用XPath来对XML文件查询,查询结果为:");    
    OldLinqToXMLQuery();    
    Console.WriteLine("使用Linq方法来对XML文件查询,查询结果为:");    
    UsingLinqLinqtoXMLQuery();    
    Console.ReadKey();    
}      
               
// 初始化XML数据    
private static string xmlString =     
            "<Persons>"+    
            "<Person Id='1'>"+    
            "<Name>张三</Name>"+    
            "<Age>18</Age>"+    
            "</Person>" +    
            "<Person Id='2'>"+    
            "<Name>李四</Name>"+    
            "<Age>19</Age>"+    
            "</Person>"+    
             "<Person Id='3'>" +    
            "<Name>王五</Name>" +    
            "<Age>22</Age>" +    
            "</Person>"+    
            "</Persons>";    
           
// 使用XPath方式来对XML文件进行查询    
private static void OldLinqToXMLQuery()    
{    
   // 导入XML文件    
   XmlDocument xmlDoc = new XmlDocument();    
   xmlDoc.LoadXml(xmlString);    
           
   // 创建查询XML文件的XPath    
   string xPath = "/Persons/Person";    
           
   // 查询Person元素    
   XmlNodeList querynodes = xmlDoc.SelectNodes(xPath);    
   foreach (XmlNode node in querynodes)    
   {    
      // 查询名字为李四的元素    
      foreach (XmlNode childnode in node.ChildNodes)    
      {    
          if (childnode.InnerXml == "李四")    
          {    
              Console.WriteLine("姓名为: "+childnode.InnerXml + "  Id 为:" + node.Attributes["Id"].Value);    
          }    
      }    
   }             
}    
           
// 使用Linq 来对XML文件进行查询    
private static void UsingLinqLinqtoXMLQuery()    
{    
   // 导入XML    
   XElement xmlDoc = XElement.Parse(xmlString);    
           
   // 创建查询,获取姓名为“李四”的元素    
   var queryResults = from element in xmlDoc.Elements("Person")    
                   where element.Element("Name").Value == "李四"
                   select element;    
                      
   // 输出查询结果    
   foreach (var xele in queryResults)    
   {    
       Console.WriteLine("姓名为: " + xele.Element("Name").Value + "  Id 为:" + xele.Attribute("Id").Value);    
   }    

  需要注意的是使用上面Linq查询得到的结果,只有当使用foreach等方法去遍历的时候查询才会真正的执行并返回结果。

  有的同学很快就发现了,这些关键字怎么看都像是Sql语句的关键字,确实,这就是语言集成查询的真正含义,查询语句可以直接在编译的时候确定语法上正确性,这一点体现最完全的就是Linq to Sql,通常也称为DLinq。Sql基本所有的关键字都可以在C#中找到想对应的关键字,所以Sql基本所有的操作在Linq中都有,比如:查询、排序、分组、增加和删除等等。这个可以通过查询MSDN(http://msdn.microsoft.com/zh-cn/library/bb397676.aspx)或者学习一些优秀的Blog(http://www.cnblogs.com/lifepoem/archive/2011/12/16/2288017.html) 来熟悉其语法。

  在.NET下与数据库交互时,为了从对象级别处理问题,必须对数据库进行抽象的到对应的对象,这个就是ORM(对象关系映射,Object/Relation Mapping)的过程。ORM按照字面的意思理解即可:ORM的过程就是把数据库中的概念,比如数据库,表,字段等,处理成语言中的对象,然后在对象级别上完成数据库的常用操作,这些操作会通关相关的机制自动的反映到数据库中。这是一个抽象的过程,这样做的好处就是处理的程序不用考虑数据库实际操作上的细节,只要在语言层次上完成相关的操作,数据库自然就会被更新。

  在.NET下ORM的手段主要是两种:DLinq和Entity Framework。这两种手段的目标都是一致的,就是ORM的那个过程,但是处理的细节(比如抽象的级别,支持的数据库,支持的程度,是否继续维护等等)有些不同,也就导致了两者不同的命运。总的来说,Entity Framework是重量级的工具,支持多种数据库,特性更多,更加抽象,更加灵活,效率更好一点,而且新版对linq的语法支持也越来越完善(DLinq不再添加新特性了,前途很明显),这真是居家旅行杀人越货必备之首选啊。

  两者的对比网上也很多,比如:http://blog.163.com/kunkun0921@126/blog/static/169204332201401605839384/

  这里想多啰嗦一句的就是Entity Framework支持所谓的Database First和Code First两种开发方式,前置是先设计数据库,后映射对象;后者是先设计对象,后生成数据库。这些开发方式还是相当灵活的,而且在VS的命令行中生成、更新数据库都有相关的命令,使用起来是相当的方便。

此外还有一些好的学习网站:

http://www.tuicool.com/topics/11050019

http://www.entlib.net/?cat=31

http://www.cnblogs.com/lsxqw2004/category/266012.html

C#的变迁史 - C# 3.0篇的更多相关文章

  1. C#的变迁史 - C# 4.0篇

    C# 4.0 (.NET 4.0, VS2010) 第四代C#借鉴了动态语言的特性,搞出了动态语言运行时,真的是全面向“高大上”靠齐啊. 1. DLR动态语言运行时 C#作为静态语言,它需要编译以后运 ...

  2. C#的变迁史 - C# 2.0篇

    在此重申一下,本文仅代表个人观点,如有不妥之处,还请自己辨别. 第一代的值类型装箱与拆箱的效率极其低下,特别是在集合中的表现,所以第二代C#重点解决了装箱的问题,加入了泛型.1. 泛型 - 珍惜生命, ...

  3. C#的变迁史 - C# 1.0篇

    C#与.NET平台诞生已有10数年了,在每次重大的版本升级中,微软都为这门年轻的语言添加了许多实用的特性,下面我们就来看看每个版本都有些什么.老实说,分清这些并没什么太大的实际意义,但是很多老资格的. ...

  4. C#的变迁史 - C# 4.0 之多线程篇

    在.NET 4.0中,并行计算与多线程得到了一定程度的加强,这主要体现在并行对象Parallel,多线程Task,与PLinq.这里对这些相关的特性一起总结一下. 使用Thread方式的线程无疑是比较 ...

  5. C#的变迁史 - C# 4.0 之线程安全集合篇

    作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了. 而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们, ...

  6. C#的变迁史 - C# 5.0 之调用信息增强篇

    Caller Information CallerInformation是一个简单的新特性,包括三个新引入的Attribute,使用它们可以用来获取方法调用者的信息, 这三个Attribute在Sys ...

  7. C#的变迁史 - C# 5.0 之并行编程总结篇

    C# 5.0 搭载于.NET 4.5和VS2012之上. 同步操作既简单又方便,我们平时都用它.但是对于某些情况,使用同步代码会严重影响程序的可响应性,通常来说就是影响程序性能.这些情况下,我们通常是 ...

  8. C#的变迁史 - C# 4.0 之并行处理篇

    前面看完了Task对象,这里再看一下另一个息息相关的对象Parallel. Parallel对象 Parallel对象封装了能够利用多核并行执行的多线程操作,其内部使用Task来分装多线程的任务并试图 ...

  9. C#的变迁史 - C# 5.0 之其他增强篇

    1. 内置zip压缩与解压 Zip是最为常用的文件压缩格式之一,也被几乎所有操作系统支持.在之前,使用程序去进行zip压缩和解压要靠第三方组件去支持,这一点在.NET4.5中已有所改观,Zip压缩和解 ...

随机推荐

  1. DNS拾遗(二)

    MX Record补充 MX记录有优先级的概念,数字越小表示优先级越高.所以一个域可以配置多个不同优先级的MX记录,如果邮件通过第一优先级记录无法递送,则采用第二优先级,以此类推. TXT Recor ...

  2. Fix catalyst driver in Ubuntu 13.04 / 13.10

    Fix catalyst driver in Ubuntu 13.04 / 13.10(墙外文章备份) 1. Introduction I found lots of people strugglin ...

  3. Linux下Java开发环境搭建—CentOS下Eclipse的安装教程

    据了解,在Linux下的Java开发很多时候都比较喜欢使用vim + 插件,反而很少使用Eclipse,但是我是第一次使用Linux来进行Java编程,就什么都体验下啦,好啦,废话不多说,直接开始啦. ...

  4. jQuery疑惑记录

    不断更新 1.项目中有一句 this.$html = $('<div/>').html(html).attr('sspa-mod-id', this.modName).hide();不知道 ...

  5. Redis学习笔记~是时候为Redis实现一个仓储了,RedisRepository来了

    回到目录 之前写了不少关于仓储的文章,所以,自己习惯把自己叫仓储大叔,上次写的XMLRepository得到了大家的好评,也有不少朋友给我发email,进行一些知识的探讨,今天主要来实现一个Redis ...

  6. python 多线程网络编程 ( 二 )

    背景 我在[第一篇文章中]已经介绍了如何实现一个多线程的todo应用,接下来我将会研究如何使这个服务器完成下面这几个功能. 1.使用正则表达式解析用户发送的请求数据: 2.使用ThreadLocal技 ...

  7. Atitit. Atiposter 发帖机 新特性 poster new feature v11  .docx

    Atitit. Atiposter 发帖机 新特性 poster new feature v11  .docx 1.1.  版本历史1 2. 1. 未来版本规划2 2.1. V12版本规划2 2.2. ...

  8. java初学者应掌握的30个基本概念

    核心提示:OOP中唯一关系的是对象的接口是什么,就像计算机的销售商她不管电源内部结构 是怎样的,他只关系能否给你提供电就行了,也就是只要知道can or not而不是how and why. 基本概念 ...

  9. C 中读取键盘码

    键盘码在底层开发中经常用到,有时候我们会忘记它们,就要急急忙忙的去找 键-码 对照表查看,其实程序可以自己打印出 键-码 对应值 #include <stdio.h> #include & ...

  10. vsCode 添加浏览器调试和js调试的方法

    1.直接按F5可以调试的方法或者点击运行按钮(可以直接运行html文件或者js文件) 在launch.json文件中的配置如下: {     "version": "0. ...