.net版本发展历程:
版本 | 版本号 | 发布日期 | Visual Studio | windows集成 |
---|---|---|---|---|
1.0 | 1.0.3705.0 | 2002-02-13 | Visual Studio .NET | |
1.1 | 1.1.4322.573 | 2003-04-24 | Visual Studio .NET 2003 | Windows Server 2003 |
2.0 | 2.0.50727.42 | 2005-11-07 | Visual Studio 2005 | |
3.0 | 3.0.4506.30 | 2006-11-06 | Windows Vista, Windows Server 2008 | |
3.5 | 3.5.21022.8 | 2007-11-19 | Visual Studio 2008 | Windows 7, Windows Server 2008 R2 |
4.0 | 4.0.30319.1 | 2010-04-12 | Visual Studio 2010 |
.NET Framework 各个版本之间的关系图:
C#2.0新特性:
一、泛型(Generic)
概述:
引入泛型绝对是C#2.0的最大的新特性。通过“参数化类型”实现一份代码操作多种数据类型。泛型的优点是更好的类型安全;更好的复用;更高的效率和更清晰的约束。但说实在,除了一些通用的数据结构,集合类和通用算法外,使用到泛型的地方还不多。而且System.Collections.Generic中的类也很够用了。基本没写过自己的泛型类。
语法点:
语法方面主要是使用在类型名称后面加入“<T>”来传入类型参数。涉及泛型类型继承的一条规则是“封闭类(所有类型都已确定)不能继承开放类(含有未确定类型)”。同时可以使用where来添加对作为参数的类型的约束。具体有四种:基类约束、接口约束、构造器约束和值/引用类型约束。最后特别提醒,delegate也是一种类型,别忘了泛型委托。
二、泛型方法(Generic Method)
概述:
泛型方法即在方法声明中包含类型参数。其实属泛型的内容。但泛型方法可以用在一般类中。
语法点:
重载(overload)的时候注意,约束不成为方法签名的一部分。而重写(override),约束是会被默认继承的。
三、匿名方法(Anonymous Method)
概述:可以直接将代码赋给委托。在进行委托实例化时可以省略掉委托类型。代码示例如下:
myButton.Click += delegate {//代码}
myButton.Click += myClick;
感觉就是一种语法的简化。从代码的可维护性来说还是不用为好。但又确实能使一些代码简洁一点。看团队的决定吧。
语法点:
要注意外部变量的生存周期会一直延长到委托实例引用结束。尽量避免使用。
四、迭代器(Iterator)
概述:
严格来说只是简化迭代器的构造罢了。或者说简化了创建可以用于foreach的枚举集合的工作。通过引入关键字yield来实现。再不用太多的关心IEnumerator了。直接在类中实现GetEnumerator()然后在for循环中用yield return就可以了。
语法点:
可以用yield break来停止迭代。
五、局部类(Partial Class)
概述:
就是允许通过关键字partial将一个类写在多个cs文件中。最大的用处可能就是将IDE自动生成的代码分开吧。大家都留意到VS05中的自动生成的代码都到designer.cs中去了吧。
语法点:
在不同部分定义中,Attribute和Interface是累加的。当然要保证基类的唯一。而abstract和sealed是部分使用,整个类生效。语义类型访问修饰符要保持一致。
六、可空类型(Nullable Type)
概述:
其实是一个泛型类型。System.Nullable<T>,T必须为值类型。并且通过“?”来作一个语法的简化。用一句代码说明就是:
int? x = null;
引入NullableType获得的启示是,.Net的ORMapping可以做得更好了。
语法点:
使用HasValue属性判断是否为null。
七、静态类(Static Class)
概述:
可以声明一个static class。该class不能被继承不能被实例化,只能包含静态成员。相当于sealed abstract。用途可以用作全局函数。另外Singleton模式的实现更方便了。
语法点:
不能有constructor,不能有实例成员,不能再使用sealed abstract修饰。不能继承其他类和实现任何interface。成员不能使用protected修饰符。
八、属性访器访问修饰符
概述:
可以额外对get、set中的其中一个属性访问其使用一个访问修饰符。用途可以说是更灵活的封装吧。
语法点:
只能在get、set的一个中使用。接口中的属性访问器不能同样不能带有访问修饰符。索引器可以使用。
九、命名空间别名限定符
概述:可以使用双冒号“::”使用一个命名空间的别名。如
using sc=System.Collection;
那么使用的时候就可以写成sc::ArrayList()了。
作用是更方便的调用不同命名空间下的同名类性。
就写这主要的九点,其他还有的一些C#2.0的新特就不写了。因为离开发的使用更远了。总的来说除了泛型,2.0真的不是有很多的有用的变化。或许更应关注Framework的发展吧。以上的是我个人的归纳,写得不详细。如果真的想了解语法各位看官可要自己再查了。2.0已远处,C#3.0已经来了,一股LINQ的学习热潮到了。继续前行,学好新技术,更关键是用好新技术。
--------------------------------------------------------------------------------------------------
C#3.0新特性:
1:隐式类型的本地变量和数组
2:对象初始值设定项
3:集合初始值设定项
4:自动实现属性
5:匿名类型
6:扩展方法
7:分部方法定义
8:Lambda 表达式
关键字(var)
1:var可以用局部变量声明不可以用于字段可以应用于for、foreach、using语句中
2:var关键字不能包含自身对象或者集合初始化器,但可以经过new的新表达式
例:var result;//编译错误
var result=result+1;//编译错误
var result={1,2,3};//编译错误
3:var是推断类型,不是显示类型
4:var关键字指示编译器根据初始化语句右侧的表达式推断变量的类型
5:推断类型可以是内置类型、匿名类型、用户定义类型、.NET Framework 类库中定义的类型或任何表达式
注:var关键字并不意味着“变体”,也不表示该变量时松散类型化变量或后期绑定变量。它只是表示由编译器确定和分配最适合的类型
var 使用的场景
1:局部变量
例:var i = 5;
2:在for初始化语句
例:for(var i = 1; i < 10; ++i)
3:在foreach初始化语句
例:foreach(var item in list)
4:在using语句
例:using(var file = new StreamReader("C:\"))
一:隐式类型的本地变量和数组
注:隐形数组使用var关键字和数组初始化器创建。元素的数据类型必须能够隐式转换为同一数据类型,并且不能为空(null)
1:没有在初始化语句的左侧隐式类型的数组使用方括号
2:支持交错数组,不支持多维数组
例:var a = new [] {1,2,3} //一维数组
var b = new []
{
new [] {1,2,3},
new [] {5,6}
}; //交错数组
二:对象初始值设定项
注:对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭
1.NET 2.0写法:
User userInfo = new User();
userInfo.ID = “zhuxing”;
userInfo.Name = “czx”;
userInfo.Age= 22;
2.NET 3.5写法:
User userInfo = new User() {ID = “zhuxing”, Name = “czx”, Age=22};
注:嵌套复杂属性类型
User userInfo = new User()
{
ID=“zhuxing”,
Name=“czx”,
Address =new Address()
{
Province=“
City=“ningde”
}
};
1:可以和构造函数一起使用,并且构造函数初始化先于对象初始化器执行
2:允许部分赋值
3:允许给internal 成员赋值
public class user
{
public String Name { get; set; }
public String Age { get; set; }
private Int32 test = 25;
internal Int32 test2;
}
public class Program
{
static void
{
user person = new user { Name = "张三", Age = "男", test2 = 20 };
Console.WriteLine("{0},{1},{2}",person.Name, person.Age, person.test2);
Console.ReadLine();
}
}
//和构造函数一起使用
public class user
{
public String Name { get; set; }
public Int32 Age { get; set; }
private Int32 test = 25;
internal Int32 test2;
public user(Int32 Age)
{
this.Age = Age;
}
}
public class Program
{
static void
{
user person = new user(20) { Name = "张三", Age = 22, test2 = 20 };
Console.WriteLine("{0},{1},{2}",person.Name, person.Age, person.test2);
Console.ReadLine();
}
}
三:集合初始值设定项
注:
1:集合初始化器由一系列集合对象组成,用逗号间隔,使用{}封闭。
2:集合初始化器会对初始化器中的元素进行按序调用ICollection<T>.Add(T)方法
例如:List<int> number=new List<int>{1,2,3,4,5};
//集合初始化器
public class parame
{
public String Name { get; set; }
public Int32 Age { get; set; }
}
public class Program
{
static void
{
IList<parame> people = new List<parame>()
{
new parame{ Name = "张三", Age = 18},
new parame{ Name = "李四", Age = 20}
};
foreach (var i in people)//var 是3.0特有的
{
Console.WriteLine("{0},{1}", i.Name, i.Age);
}
Console.ReadLine();
}
}
四:自动实现属性
1、.NET2.0下写User类:
public class User
{
private string id;//用户ID
public string ID
{
get{return id;}
Set {id=value;}
}
private string name;//用户名称
public string Name
{
get{return name;}
set{name=value;}
}
}
2、.NET 3.5下写User类:
public class User
{
public string ID{get;set;}
public string Name{get;set;}
}
注:在VS2008像以上写法,编译器自动会为类中生成一个私有变量,并对这个变量实现公开的getter和setter访问器
五:匿名类型
注:使用new操作符和匿名对象初始化器创建一个新的对象,该对象是匿名类型的对象。
如:
var role=new{ID=“zhuxing”,Name=“czx”};
等同于:
class _Anonymous1
{
private string id;
public string ID
{
get{return id;}
set{id=value;}
}
private string name;
public string Name
{
get{return name;}
set{name=value;}
}
}
1:匿名类型提供了一种方便的方法,可以用来将一组只读属性封装到单个对象中,而无需首先显示定义一个类型
2:类型名由编译器生成,并且不能在源代码级使用
3:这些属性的类型由编译器判定
4:匿名类型通常用在查询表达式select子句中,以便返回源序列化对象的属性子集
5:匿名类型是使用new运算符和对象初始值设定项创建的
6:匿名类型是由一个或多个只读属性组成的类类型。不允许包含其他种类的类型
(如方法或事件)
7:匿名类型是直接从对象派生的引用类型。尽管应用程序无法访问匿名类型,但编译器仍会为其提供一个名称。
8:如果两个或更多个匿名类型以相同的顺序具有相同数量和种类的 如果两个或更多个匿名类型以相同的顺序具有相同数量和种类的属性,则编译器会将这些匿名类型视为相同的类型,并且它们共享编译器生成的相同类型信息。匿名类型具有方法范围 – 匿名类型具有方法范围。
9:匿名类型不能像属性一样包含不安全类型。
10:由于匿名类型上的 Equals 和 GetHashCode 方法是根据属性的 由于匿名类型上的Equals 和 GetHashCode 方法是根据属性的Equals 和 GetHashcode 定义的,因此仅当同一匿名类型的两个
注:查询表达式经常使用匿名类型,而这些类型只能使用对象初始值设定项进行初始化
例:var productInfos = from p in products
select new { p.ProductName, p.UnitPrice };
创建匿名类型时重命名字段:
select new {p.ProductName, Price = p.UnitPrice};
六:扩展方法
1、扩展方法的优势
1.1、允许开发人员往一个现有的CLR类型的公开契约(contract)中添加新的方法,而不用生成子类或者重新编译原来的类型。
1.2、可以通过使用实例方法语法调用的静态方法 ,对现有类功能进行扩充,从而使该类型的实例具有更多的方法(功能)。
1.3、允许我们在不改变源代码的情况下扩展(即添加不能修改)现有类型中的实例方法
1.4、有助于把今天动态语言中流行的对duck typing的支持之灵活性,与强类型语言之性能和编译时验证融合起来
2、扩展方法的要点
2.1、本质为将实例方法调用在编译期改变为静态类中的静态方法调用,具备静态方法的功能
2.2、作用域是整个namespace可见的,并且可以通过using namespace来导入其它命名空间中的扩展方法。
2.3、优先级:现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后为较远的namespace下的静态类的静态方法
七:分部方法定义
注:处理一些轻量级的事件
八:Lambda 表达式
1、格式如下:(参数列表)=>表达式或语句块 ;
例: var str=list.FindAll(s=>s.indexof(“mzc”)>=0);
2、参数列表可以是显示或者隐式类型,在显式列表中,每个参数的类型是显式指定的,在隐式列表中,参数的类型由Lambda表达式出现的语境自动推断类型 。
例:
(x, y) => x * y;//多参数,隐式类型=>表达式
x => x * 10;//单参数,隐式类型=>表达式
x => { return x * 10; }; //单参数,隐式类型=>语句块
(int x) => x * 10;//单参数,显式类型=>表达式
(int x) => { return x * 10; };//单参数,显式类型=>语句块
( ) => Console.WriteLine(); //无参数
表达式树
注:表达式树允许像处理数据一样对Lambda表达式进行读取和改写,比如我们在动态查询的时候经常应用到
------------------------------------------------------------------------------------------------------
C#3.5新特性:
Visual Studio 2008和.NET 3.5是建立在.NET2.0核心的基础之上,C# 3.0新语言特性在.NET2.0基础上进行了改进,这些改进的功能可以大大简化我们编写程序。在此为了自己学习记录下来,并与大家分享
.NET 3.5的新特性包括:
自动属性(Auto-Implemented Properties)
隐含类型局部变量(Local Variable Type Inference)
匿名类型(Anonymous Types)
对象与集合初始化器(Object and Collection Initializers)
扩展方法(Extension Methods)
Lambda表达式和Lambda表达式树 (Lambda Expression and Lambda Expression Trees)
LINQ,语言级集成查询(Language INtegrated Query)
自动属性(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; }
}
我们可以利用VS.NET提供的ildasm进行反编译,可以发现.NET帮我定义了私有变量,在此不再赘言。
隐含类型局部变量(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 };//错误
注意事项:
必须对var声明的变量赋值,因为C#中的var并不等同于VB于Javascript中的var,后者中 的var为一种变量类型,而在C#中var并不是一种类型,它只用来隐式的声明变量,而你定义的变量类型由编译器判断;
Var声明的变量不能赋null;
Var只能声明局部变量;
不允许,改变var变量的类型
Var x = 100;
X = "Hello world!"
由此可知,var的变量也是强类型的变量,具有安全性。
匿名类型(Anonymous Types)
匿名类型允许定义行内类型,无须显式定义类型。常和var配合使用来声明匿名类型。
var p1 = new { Id = 1, Name = "tony", Age = 21 };//属性也不需要申明
var p2 = new { Id = 2, Name = "dream", Age = 21 };
var p3 = new { Id =3 , age = 21, Name = "tony"}
p1 = p2;//p1,p2结构相同,可以互相赋值 ,p1与p3不是相同的对象,所以使用匿名类型时我们尽量将具有相同属性的变量,属性的顺序保持一致。
在这里编译器会认为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 = "tony", Age = 21 },
new { Name = "dream", Age = 22 }
};
var a = intArray[0];
var b = strArray[0];
var c = anonymousTypeArray[1].Name;
可以使用new关键字调用匿名初始化器创建一个匿名类型的对象。
匿名类型直接继承自System. Object。
匿名类型的成员是编译器根据初始化器推断而来的一些读写属性。
对象初始化器 (Object Initializers) :
.NET2.0框架中的类型非常依赖于属性。当生成对象实例和使用新的类型时,在.Net2.0时候我们像这样写:
User user = new User();
user.Id = 1;
user.Name = "tony";
user.Age = 22;在VS2008中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。我们可以这样简化:像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。
User user = new User { Id = 1, Name = "tony", Age = 21 };又例如,我把二个人加到一个基于泛型的类型为User的List集合中:
List<User> user = new List<User>{
new User{Id=1,Name="tony",Age=21},
new User{Id=2,Name="dream",Age=12},
};
如果有相同名字和类型的两个对象初始化器将会产生相同的实例,可以相互赋值。例如:
User user = new User { Id = 1, Name = "tony", Age = 21 };
User user2 = new User { Id = 2, Name = "tony", Age = 12 };
user = user2;
除了在初始化类时设置简单的属性值外,对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。例如我们可以在上面定义的User类型同时拥有一个属于School类型的叫"School"的属性:
User user = new User
{
Id = 1,
Name = "tony",
Age = 21,
School = new School
{
City = "Beijing",
Name = "BTBU"
}
};
集合初始化器(Collection Initializers):
集合初始化器由一系列集合对象组成,用逗号间隔,使用{}封闭。
集合初始化器可以简化把几个对象一起添加到一个集合,编译器会自动为你做集合插入操作。例如我把七个数加到一个基于泛型的类型为int的List集合中
List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };对象与集合初始化器要点
对象初始化器实际上利用了编译器对对象中对外可见的字段和属性进行按序赋值。
对象初始化器允许只给一部分属性赋值,包括internal访问级别
对象初始化器可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行。
集合初始化器会对初始化器中的元素进行按序调用ICollection<T>.Add(T)方法,所以只有具有Add方法的类才可以使用这种方法添加一个元素,例如ArrayList等,例如HashTable,字典等就不支持这种添加方式,因为其存在key,value两个值。
注意对象初始化器和集合初始化器中成员的可见性和调用顺序。
对象与集合初始化器同样是一种编译时技术。
扩展方法(Extension Methods)
往往我们需要对CLR类型进行一些操作,但苦于无法扩展CLR类型的方法,只能创建一些helper方法,或者继承类。我们来修改上面的User类:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Read()
{
return "Id:" + Id + "姓名:" + Name + "年龄:" + Age;
}
}然后调用
var user = new { Id = 1, Name = "tony", Age = 21 };
var str = user.Read();现在有了扩展方法就方便多了。
扩展方法允许开发人员往一个现有的CLR类型的公开契约(contract)中添加新的方法,而不用生成子类或者重新编译原来的类型。扩展方法有助于把今天动态语言中流行的对duck typing的支持之灵活性,与强类型语言之性能和编译时验证融合起来。
扩展方法是可以通过使用实例方法语法调用的静态方法。效果上,使得附加的方法扩展已存在类型和构造类型成为可能。他可以对现有类功能进行扩充,从而使该类型的实例具有更多的方法(功能)。
扩展方法允许我们在不改变源代码的情况下扩展(即添加不能修改)现有类型中的实例方法。
扩展方法给我们一个怎样的思路呢?我们一步一步做一下!
首先声明扩展方法:通过指定关键字this修饰方法的第一个参数。注意扩展方法仅可声明在静态类中。扩展方法具备所有常规静态方法的所有能力,可以使用实例方法语法来调用。接着就可以调用扩展方法了。下面通过一个具体的实例分析一下:
例如我们要检查一个字符串变量是否是合法的电子邮件地址?在.Net2.0框架下像这样:
var email = "tony_wanghongchen@hotmail.com";
if (EmailValidator.IsValid(email))
{
Response.Write("tony提示:这是一个正确的邮件地址");
}而使用扩展方法的话,我可以添加"IsValidEmailAddress()"方法到string类本身中去,该方法返回当前字符串实例是否是个合法的字符串。
if (email.IsValidEmailAddress())
{
Response.Write("tony提示:这是一个正确的邮件地址");
}我们是怎么把这个IsValidEmailAddress()方法添加到现有的string类里去的呢?先定义一个静态类,再定义"IsValidEmailAddress"这个静态的法来实现的。
public static class Extensions//静态类
{
public static bool IsValidEmailAddress(this string s)
//静态方法和this
{
Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
return regex.IsMatch(s);
}
}注意,上面的静态方法在第一个类型是string的参数变量前有个"this"关键词,这告诉编译器,这个特定的扩展方法应该添加到类型为"string"的对象中去。然后在IsValidEmailAddress()方法实现里,我可以访问调用该方法的实际string实例的所有公开属性/方法/事件,取决于它是否是合法电子邮件地址来返回true/false。
扩展方法不仅能够应用到个别类型上,也能应用到.NET框架中任何基类或接口上。即可用于整个.NET框架丰富的可组合的框架层扩展。
扩展方法要点
扩展方法的本质为将实例方法调用在编译期改变为静态类中的静态方法调用。事实上,它确实拥有静态方法所具有的所有功能。
扩展方法的作用域是整个namespace可见的,并且可以通过using namespace来导入其它命名空间中的扩展方法。
扩展方法的优先级:现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后为较远的namespace下的静态类的静态方法。
扩展方法是一种编译时技术,注意与反射等运行时技术进行区别,并慎重使用。
Lambda表达式和Lambda表达式树 (Lambda Expression and Lambda Expression Trees)
Lambda表达式
我们从"所有字符串查找包含tony子字符串"说起。在C# 2.0中,匿名方法允许我们以内联的方式来实现委托实例,它提供强大的函数式编程语言,但是标记显得相当的冗长和带有强制性。我们使用C# 2.0 中的匿名方法查找,代码如下:
var inString = list.FindAll(delegate(string s)
{ return s.Indexof("tony") >= 0; });现在可以使用C# 3.0带来的Lambda表达式允许我们使用一种更接近人的思维、更自然的方式来实现类似于匿名方法同样的效果,看下面的代码多么简洁:
var inString = list.FindAll(s => s.Indexof("tony") >= 0);Lambda表达式格式:(参数列表)=>表达式或语句块
具体意义:定义Lambda接受参数列表,运行表达式或语句块返回表达式或语句块的值传给这个参数列表。
Lambda表达式参数类型可以是隐式类型或显式类型。在显式列表中,每个参数的类型是显式指定的,在隐式列表中,参数的类型由Lambda表达式出现的语境自动推断类型。
Lambda表达式的参数列表可以有一个或多个参数,或者无参数。在有单一的隐型参数的lambda表达式中,圆括号可以从参数列表中省略。
例如:
(x, y) => x * y;//多参数,隐式类型=>表达式
x => x * 10;//单参数,隐式类型=>表达式
x => { return x * 10; }; //单参数,隐式类型=>语句块
(int x) => x * 10;//单参数,显式类型=>表达式
(int x) => { return x * 10; };//单参数,显式类型=>语句块
() => Console.WriteLine(); //无参数下面看这个例子:
在前面的帖子中,我们写了一个User类及增加了2个人,接下来,我们使用由LINQ提供的新的Where和Average方法来返回集合中的人的一个子集,以及计算这个集合中的人的平均年龄:
List<User> user = new List<User>{
new User{Id=1,Name="tony",Age=21},
new User{Id=2,Name="tony",Age=22},
};
//获取特定人时所用的过滤条件,p参数属于User类型
var results = user.Where(p => p.Name == "tony").ToList();
//用User对象的Age值计算平均年龄
var average = user.Average(p => p.Age);
LINQ,语言级集成查询(Language INtegrated Query)
经过了最近 20 年,面向对象编程技术( object-oriented (OO) programming technologies )在工业领域的应用已经进入了一个稳定的发展阶段。程序员现在都已经认同像类(classes)、对象(objects)、方法(methods)这样的语言特性。考察现在和下一代的技术,一个新的编程技术的重大挑战开始呈现出来,即面向对象技术诞生以来并没有解决降低访问和整合信息数据(accessing and integrating information)的复杂度的问题。其中两个最主要访问的数据源与数据库( database )和 XML 相关。
LINQ 提供了一条更常规的途径即给 .Net Framework 添加一些可以应用于所有信息源( all sources of information )的具有多种用途( general-purpose )的语法查询特性( query facilities ),这是比向开发语言和运行时( runtime )添加一些关系数据( relational )特性或者类似 XML 特性( XML-specific )更好的方式。这些语法特性就叫做 .NET Language Integrated Query (LINQ) 。
包含 DLinq 和 XLinq
C#3.0 LINQ 查询语法
首先来看一个很简单的LINQ查询例子,查询一个int 数组中小于5的数字,并按照大小顺序排列:
class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 8, 5, 89, 3, 56, 4, 1, 58 };
var m = from n in arr where n < 5 orderby n select n;
foreach (var n in m)
{
Console.WriteLine(n);
}
Console.ReadLine();
}
}
上述代码除了LINQ查询语法外,其他都是我们所熟悉的语法,而LINQ查询语法跟SQL查询语法很相似,除了先后顺序。
Q:为何 LINQ 查询语法是以 from 关键字开头的,而不是以 select 关键字开头的?select 开头这种写法跟SQL的写法更接近,更易懂呀?
A:简单来说,为了IDE的智能感知(InteliSence)这个功能,select 关键字放在后面了。
编程语言以 select 开头写LINQ查询语法不是没出现过,你如果使用过2005年的VB9 CTP 版本,那时候VB9的LINQ查询语法就是 select 关键字在前面,但是 select 关键字在前面,在做智能感知(InteliSence)时候就很头大。经过微软IDE组的权衡,确定了把 from 关键字放在最前面。
我们再来看一个稍稍复杂的LINQ查询:
在我们罗列的语言字符串中,我们希望按照字符长短,分类罗列出来,实现代码如下:
static void Main(string[] args)
{
string [] languages = {"Java","C#","C++","Delphi","VB.net","VC.net","C++ Builder","Kylix","Perl","Python"};
var query = from item in languages
orderby item
group item by item.Length into lengthGroups
orderby lengthGroups.Key descending
select lengthGroups;
foreach (var item in query)
{
Console.WriteLine("strings of length ",item.Key);
foreach (var val in item)
{
Console.WriteLine(val);
}
}
Console.ReadLine();
}
其中的 into 关键字表示将前一个查询的结果视为后续查询的生成器,这里是跟 group by一起使用的。
LINQ中的Group by不要跟 SQL 中的Group by 混淆,SQL 由于是二维结构,Group by 的一些逻辑受二维结构的约束,无法象 LINQ 中的Group by 这么灵活。
-------------------------------------------------------------------------------------
- 简介
- C# 4.0
- 动态查找
- 类型
- 动态操作
- 运行时查找
- 示例
- 带有动态参数的重载解析
- 动态语言运行时
- 已知问题
- 命名参数和可选参数
- 可选参数
- 命名的和可选的实参
- 重载解析
- 互操作特性
- 动态引入
- 无PIA的编译
- ref 省略ref
- 已知问题
- 变性
- 协变性
- 逆变性
- 限制
- COM Example COM示例
- Relationship with Visual Basic 与Visual Basic的关系
- 资源