C#.net 3.5新特性介绍
一、扩展方法
必备条件:
1、类为静态类
2、方法为静态方法
3、其参数o为object类型
4、有关键字this进行修饰
在定义了该类的同一命名空间下的其他类中即可对所有类型使用该Name方法来获取它的ToString()之后的值。我们看编译器是如何来表示的:
我们看到,编译器强大的智能提示功能给我们的提示是(extension) string object.Name(),这是因为我们是对object类进行了扩展,故我们可以对任何类型使用扩展方法Name。
二、自动属性
get和set关键字均没有我们以前所熟悉的return value;和_privateField=value;这样的字眼。事实上,这就是善解人意的编译器为我们提供的又一项新功能--自动属性。编译器会为我们定义的Name属性自动生成一私有变量来保存其值。于是,我们原来需要至少三行才能完成的代码现在仅需一行便轻松完成了。
不过,它有自身的局限性,比如说不能在用自动属性定义的属性中加逻辑判断,get和set必须成对出现等。然而,笔者相信并非我们所有的属性均要加上逻辑判断吧?那么,就请去尝试自动属性为我们带来的便利。
publice int Name { get; set;}
三、对象初始化器
对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。
Useruser = newUser{ Id = 1, Name = "YJingLee", Age = 22 };
又例如,我把二个人加到一个基于泛型的类型为User的List集合中:
List<User> user = newList<User>{
newUser{Id=1,Name="YJingLee",Age=22},
newUser{Id=2,Name="XieQing",Age=25},
};
如果有相同名字和类型的两个对象初始化器将会产生相同的实例,可以相互赋值。例如:
Useruser = newUser{ Id = 1, Name = "YJingLee", Age = 22 };
Useruser2 = newUser{ 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
}
};
四、集合初始化器
集合初始化器由一系列集合对象组成,用逗号间隔,使用{}封闭。
集合初始化器可以简化把几个对象一起添加到一个集合,编译器会自动为你做集合插入操作。例如我把七个数加到一个基于泛型的类型为int的List集合中
List<int> num = newList<int> { 0, 1, 2, 6, 7, 8, 9 };
² 对象与集合初始化器要点
1 对象初始化器实际上利用了编译器对对象中对外可见的字段和属性进行按序赋值。
2 对象初始化器允许只给一部分属性赋值,包括internal访问级别
3 对象初始化器可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行。
4 集合初始化器会对初始化器中的元素进行按序调用ICollection<T>.Add(T)方法。
5 注意对象初始化器和集合初始化器中成员的可见性和调用顺序。
6 对象与集合初始化器同样是一种编译时技术。
五、匿名类型
匿名类型允许定义行内类型,无须显式定义类型。常和var配合使用来声明匿名类型。
varp1 = new{ Id = 1, Name = "YJingLee", Age = 22 };//属性也不需要申明
varp2 = new{ Id = 2, Name = "XieQing", Age = 25 };
p1 = p2;//p1,p2结构相同,可以互相赋值
在这里编译器会认为p1,p2相当于:
public classSomeType
{
public intId { get; set; }
public stringName { get; set; }
public intAge { get; set; }
}
那么数组怎么定义呢?使用"new[]"关键字来声明数组,加上数组的初始值列表。像这样:
varintArray = new[] { 2, 3, 5, 6 };
varstrArray = new[] { "Hello", "World"};
varanonymousTypeArray = new[]
{
new{ Name = "YJingLee", Age = 22 },
new{ Name = "XieQing", Age = 25 }
};
vara = intArray[0];
varb = strArray[0];
varc = anonymousTypeArray[1].Name;
²
1 可以使用new关键字调用匿名初始化器创建一个匿名类型的对象。
2 匿名类型直接继承自System. Object。
3 匿名类型的成员是编译器根据初始化器推断而来的一些读写属性。
六、隐式类型本地变量
.NET Framework中有一种新的特性,叫做.NET Framework隐式类型变量。我们会在这篇文章中详细的为大家介绍相关知识。使用vs 2008,可以声明一个变量,让编译器隐式地决定该变量的类型。LINQ就是使用这个功能处理所创建的变量的。要使用这个功能,需要使用var关键字:
var x=5;
使用这个语句时,编译器会使用5来确定该变量的类型。这表示,该语句实际上应该如下表示:
int x=5;
.NET Framework隐式类型变量是强类型的,在编译器第一次编译后var就会被确定的类型所替代的.
隐式类型化的变量关键字与js中的var:
javascript是弱类型的语言,而且javascript中的变量(也包括用var声明的变量)可以变换类型,如下面的javascript所示:
1 var s = "abcd";
2 s=3;
3 alert(s);
上面的代码第一次给s赋了一个字符串,而第二行代码又给赋了一个整数。这样的代码在javascript中没有任何问题。但在C#3.0中,var变量一但被初始化,确定类型后,就无法改变类型了。如下面的代码是无法编译通过的:
4 var ss = "abcd";
5 ss = 44;
综上所述,在使用.NET Framework隐式类型变量var定义变量时有以下四个特点:
1.必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式:
6 var s;
7 s = “abcd”;
它是编译器根据上下文推断出来的,所以所有一切不能被编译器推断出来的用法都是错误的。比如不能这样使用:var nullValue = null;因为null啥也不是,他是一个空指针,是一个不确定的东西。也不能这样使用:var I = 5;I = “abc”;编译器根据第一个赋值会推断出它是一个整型,但是随后又将一个字符串赋值给它,这是怎么回事呢?
2.var要求是局部变量。
3.使用.NET Framework隐式类型变量var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。但笔者建议如果事先知道变量的类型,尽量使用强类型方式来声明变量。否则,就会造成由于大量使用var,而使得开发人员很难断定某个变量是什么类型。这样不利于程序的维护和升级。(只在编译器可推断而人不可推断的时候才使用隐式类型局部变量,靠我们人工可以推断的还是不建议使用,显式的声明变量类型可以增强代码的可读性,这是一个好的编程习惯,不要因为C# 3.0提供了这样的特性就大用而特用。)
虽然.NET Framework隐式类型变量var有利有弊,但笔者个人认为,如果将动态语言转换成C#语言,可以考虑使用var来定义变量。这是因为动态语言没有类型,而要将其转换成强类型的C#语言,就必须给变量指定个类型,但事先确定类型是很费劲的,不如将其指定成var,再由C#编译器去确定变量的具体类型。那么如果在转换的过程中,发现动态语言的变量改变了类型,该怎么办呢?这个可以使用“匿名类”来解决这个问题。
七、Lambda表达式
奇怪的"=>"符号--lambda表达式
且不说其他部分意义,单来看我们.Where括号之中的部分,c=>c.Address==City.Heze这段代码,我们可以将它理解为,给定c,返回c.Address==City.Heze的记录集,此处就是lambda表达式的应用之一,它广泛应用于我们下期即将介绍的LINQ(Language Integerated Query)中。