原文摘自百度百科http://baike.baidu.com/view/406857.htm
以下内容,部分已被修改为自己容易理解的文字,若有不明之处,请参考本文出处。
C# DotNet 2.0新特性
1、 泛型 2、 匿名方法 3、迭代器 4、局部类型 5、空属类型 6、静态类
C# DotNet 3.0新特性
第一个特性:隐式类型化本地变量
第二个特性:匿名类型
第三个特性:隐式类型化数组
第四个特性:对象构造者
第五个特性:集合构造者
第六个特性:Lambda表达式
第七个特性:扩展方法
第八个特性:Linq查询表达式
1、泛型(generics)
使用泛型,可以定义使用类型参数定义的类和/或方法。当客户端代码实例化类型时,会指定一个特定的类型作为参数。
/*
* 泛型可以使一般的集合仍是强类型化——出错的几率更小(因为出错在运行期间发生,提高性能,在使用集合时将可以使用Intellisense特性
* 要在代码中使用泛型,需要引用System.Collections.Generic命名空间,该命名空间还允许访问Stack、Dictionary、SortedDictionary、List和Queue类的泛型版本。
* 下面演示Stack类的泛型版本:
* 在下面的例子中,Stack类显示转换为string类型。这里括号指定集合类型。这个例子使用Stack<string>把Stack类转换为string类型。如果要把它转换为不是string类型的Stack类,例如int,就可以指定Stack<int>。
* 在创建Stack类时,Stack类中的项集合就转换为指定的类型,所以Stack类不再把项集合转换为object类型,以后(在foreach循环中)再转换为string类型。这个过程称为装箱,该过程是很昂贵的。因为这段代码事先指定了类型,所以使用集合的性能提高了。
* 泛型除了用于处理集合类型之外,还可以用于处理类、委托、方法等。
*/
static void Main(string[] args)
{
Stack<string> myStack = new Stack<string>();
myStack.Push("a");
myStack.Push("b");
myStack.Push("c");
Array myArray = myStack.ToArray();
foreach (string item in myArray)
{
Console.WriteLine(item);
}
}
2、匿名方法
匿名方法是作为参数传递给委托的代码块。
/*
* 匿名方法可以把编程步骤放在一个委托中,以后再执行该委托,而不是创建全新的方法。
* 例如不使用匿名方法,就要以下面的方式使用委托:
*/
protected void Page_Load(object sender, EventArgs e)
{
this.Button1.Click += ButtonWork;
}
void ButtonWork(object sender, EventArgs e)
{
Label1.Text = "You clicked the button!";
}
/*
* 但若使用匿名方法,就可以把这些操作直接放在委托中,如下面的例子所示:
*/
protected void Page_Load(object sender, EventArgs e)
{
this.Button1.Click += delegate(object myDelSender, EventArgs myDelEventArgs)
{
Label1.Text = "You clicked the button!";
};
}
在使用匿名方法时,不需要创建另外一个方法,而是把需要的代码直接放在委托声明后面。委托执行的语句和步骤放在花括号中,用一个分号结束。
3、迭代器(iterator)
迭代器是这样一种方法,它允许包含集合或数组的类的使用者使用 foreach,in循环访问该集合或数组。
/*
* 迭代器允许在自己的定制类型中使用foreach循环。为此,需要使类实现IEnumberable接口,如下所示:
*/
public class myList
{
public IEnumerator GetEnumerator()
{
yield return "a";
yield return "b";
yield return "c";
}
}
//要使用IEnumerable接口,需要引用System.Collections命名空间。有了该命名空间,就可以迭代定制类了,如下所示:
protected void Page_Load(object sender, EventArgs e)
{
myList IteratorList = new myList();
foreach (string item in IteratorList)
{
Response.Write(item.ToString() + "<br/>");
}
}
4、局部类型
局部类型允许我们将一个类的代码分别写在不同的cs文件中。最典型的应用就是使用VS2005创建Form文件时,VS会自动将系统生成的代码与用户代码分开。局部类型通过partial关键字来声明。
/*
* 部分类是.NET Framework 2.0的一个新功能,C#也充分利用这一功能。部分类可以把单个类分解到多个类文件中,以后在编译时再把这些文件合并起来。
* 要创建部分类,只需给要与另一个类合并起来的类使用partial关键字。在要与最初的类合并的类中,partial关键字放在class关键字的前面。例如:有一个简单的类Calculator,如下所示:
*/
public partial class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
//接着创建第二个类,它要与第一个类关联起来,如下面的例子所示:
public partial class Calculator
{
public int Subtract(int a, int b)
{
return a - b;
}
}
class Program
{
static void Main(string[] args)
{
Calculator theCalculator = new Calculator();
Console.WriteLine(theCalculator.Subtract(2, 1));
Console.WriteLine(theCalculator.Add(1, 2));
}
}
5、空属类型
空属类型是一种像int,一样可以为空的变量类型。本质上是一种泛型的应用,是System .Nullable<>的一种类型实例化。 由于泛型引入到底层的.NET Framework2.0中,所以现在可以使用System.Nullable<T>创建可空的值类型。这非常适合于创建int类型的可空项集合。
static void Main(string[] args)
{
/*
* 要创建int类型的可空类型,可以使用下面的语法:
*/
Nullable<int> x = new Nullable<int>();
//有一个新的类型修饰符,可用于创建可空类型,如下面的例子所示:
int? salary=800000;
salary = null;
Console.WriteLine(x);
Console.WriteLine(salary);
}
6、静态类
静态类是只用于包含静态成员的类型,它既不能实例化,亦不能被继承。
第一个特性:隐式类型化本地变量
这个特性非常简单,有些JavaScript的影子,我们可以统一使用使用"var"关键字来声明局部变量,而不再需要指明变量的确切类型了,变量的确切类型可通过声明变量时的初始值推断出来。这样一来,可以大大简化我们声明局部变量的工作量了。
var a=5;相当于 int a=5;
第二个特性:匿名类型
有些时候我们需要临时保存一些运算的中间结果,特别是当这些中间结果是由多个部份组成时,我们常常会去声明一个新的类型,以方便保存这些中间结果。表面上看起来这很正常,而细想之后就会发现,这个新类型只服务于这个函数,其它地方都不会再使用它了,就为这一个函数而去定义一个新的类型,确实有些麻烦。现在,C#3.0中的匿名类型特性就可以很好的解决上面提到的问题,通过匿名类型,我们可以简单使用new { 属性名1=值1, 属性名2=值2, ..... , 属性名n=值n }的形式直接在函数中创建新的类型。
定义:var a=new {name="b",age=15};
使用:a.name的值为字符串"b" ,a.age的值为整型15
第三个特性:隐式类型化数组
这个特性是对隐式类型化本地变量的扩展,有了这个特性,将使我们创建数组的工作变得简单。我们可以直接使用"new[]"关键字来声明数组,后面跟上数组的初始值列表。在这里,我们并没有直接指定数组的类型,数组的类型是由初始化列表推断出来的。
定义:var a=new[]{new{name="b"}};
使用:a[0].name的值为字符串"b"
第四个特性:对象构造者
C#3.0中加入的对象构造者特性,使得对象的初始化工作变得格外简单,我们可以采用类似于数组初始化的方式来初始化类的对象,方法就是直接在创建类对象的表达式后面跟上类成员的初始化代码。
定义:
class Point
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()//重写ToString方法
{
return "(" + X.ToString() + ", " + Y.ToString() + ")";
}
}
我们在定义Point类的X和Y属性时,只须写上该属性的get和set访问器声明,C#编译器会自动为我们生成默认的get和set操作代码,当我们需要定义简单属性时,这个特性非常有用。
初始化:
Point thePoint = new Point() { X = 1, Y = 2 };
使用:
Console.WriteLine("Point(X, Y) = "+ thePoint.ToString());
上篇文章中介绍了C# 3.0中比较简单的四个特性,分别是隐式类型化本地变量、匿名类型、隐式类型化数组,以及对象构造者,下面我将对C# 3.0中的较复杂,同时也是非常强大的几个特性进行介绍,供大家快速浏览。
第五个特性:集合构造者
C# 3.0中加入的集合构造者特性,可使我们享受到与普通数组一样的待遇,从而在创建集合对象的同时为其指定初始值。为了做到这一点,我们需要让我们的集合实现ICollection<T>接口,在这个接口中,完成初始化操作的关键在于Add函数,当我使用初始化语法为集合指定初始值时,C#编译器将自动调用ICollection<T>中的Add函数将初始列表中的所有元素加入到集合中,以完成集合的初始化操作。
定义:
class StringCollection : ICollection<string>
{
public void Add(string item)
{
Console.Write(item+"\n");
}
#region ICollection<string> 成员
public void Clear()
{
}
public bool Contains(string item)
{
return true;
}
public void CopyTo(string[] array, int arrayIndex)
{
}
public int Count
{
get;
set;
}
public bool IsReadOnly
{
get;
set;
}
public bool Remove(string item)
{
return false;
}
#endregion
#region IEnumerable<string> 成员
public IEnumerator<string> GetEnumerator()
{
List<string> theList = new List<string> { "a" };
return theList.GetEnumerator();
}
#endregion
#region IEnumerable 成员
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
使用:
StringCollection strings = new StringCollection() { "van's", "Brog", "Vicky" };
在这个示例中,编译器会自动为strings对象调用Add方法,以将初始值列表中的所有元素加入到集合中,这里我们只是简单将初始值列表中的元素输出到控制台。
第六个特性:Lambda表达式
C# 2.0中加入的匿名代理,简化了我们编写事件处理函数的工作,使我们不再需要单独声明一个函数来与事件绑定,只需要使用delegate关键字在线编写事件处理代码。
而C# 3.0则更进一步,通过Lambda表达式,我们可以一种更为简洁方式编写事件处理代码,新的Lambda事件处理代码看上去就像一个计算表达式,它使用"=>"符号来连接事件参数和事件处理代码。我可以这样写:SomeEvent += 事件参数 => 事件处理代码;
第一种方法:(使用匿名代理)
public static event EventHandler myEvent;
static void Main(string[] args)
{
myEvent += delegate(object s, EventArgs e){Console.Write(s);};
myEvent("a\n", null);
}
第二种方法:(使用Lambda表达式)
public static event EventHandler myEvent;
static void Main(string[] args)
{
myEvent += (s, e) => { Console.Write(s); };
myEvent("a\n", null);
}
第三种方法:(使用Lambda表达式)
delegate T AddDelegate<T>(T a, T b);
static void Main(string[] args)
{
AddDelegate<string> add = (a, b) => a + b;
Console.WriteLine(add("Lambda", "Expression"));
}
第四种方法:(第三种方法的通俗更改)
delegate string UseDelegate(string a, string b);
static void Main(string[] args)
{
UseDelegate use = (a, b) => a + b;
Console.WriteLine(use("Lambda", "Expression"));
}
以上方法分别使用了匿名代理和Lambda表达式来实现同样的功能,可以明显看出Lambda表达式的实现更为简洁。我们在使用Lambda表达式编写事件处理代码时,无需指明事件参数的类型,且返回值就是最后一条语句的执行结果。
第七个特性:扩展方法
当我们需要对已有类的功能进行扩展时,我们通常会想到继承,继承已有类,然后为其加入新的行为。
而C# 3.0中加入的扩展方法特性,则提供了另一种实现功能扩展的方式,我们可以在不使用继承的前提下实现对已有类本身的扩展,这种方法并不会产生新的类型,而是采用向已有类中加入新方法的方式来完成功能扩展。
在对已有类进行扩展时,我们需将所有扩展方法都写在一个静态类中,这个静态类就相当于存放扩展方法的容器,所有的扩展方法都可以写在这里面。而且扩展方法采用一种全新的声明方式:public static 返回类型 扩展方法名(this 要扩展的类型 sourceObj [,扩展方法参数列表]),与普通方法声明方式不同,扩展方法的第一个参数以this关键字开始,后跟被扩展的类型名,然后才是真正的参数列表。(第二个参数开始不需要使用this关键字)
static class Extensions
{
public static int ToInt32(this string source)
{
return Int32.Parse(source);
}
public static T[] Slice<T>(this T[] source, int index, int count)
{
if (index < 0 || count < 0 || index + count > source.Length)
{
throw new ArgumentException();
}
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}
static void Main(string[] args)
{
string number = "123";
Console.WriteLine(number.ToInt32());
int[] intArray = new int[] { 1, 2, 3 };
intArray = intArray.Slice(1, 2);
foreach (var i in intArray)
{
Console.WriteLine(i);
}
}
在上面的示例中,静态的Extensions类中有两个扩展方法,第一个方法是对string类的扩展,它为string类加入了名为ToInt32的方法,该方法没有参数,并返回一个int类型的值,它将完成数字字符向整数的转换。有了这个扩展方法之后,就可对任意string类的对象调用ToInt32方法了,该方法就像其本身定义的一样。
第二个扩展方法是一个泛型方法,它是对所有数组类型的扩展,该方法完成数组的切片操作。
C# 3.0中的Linq表达式,就是大量运用扩展方法来实现数据查询的。
第八个特性:Linq查询表达式
C# 3.0中加入的最为复杂的特性就是Linq查询表达式了,这使我们可直接采用类似于SQL的语法对集合进行查询,这就使我们可以享受到关系数据查询的强大功能。
Linq查询表达式是建立在多种C# 3.0的新特性之上的,这也是我为什么最后才介绍Linq的原因。
var persons = new[]
{
new { Name="Van's", Sex=false, Age=22 },
new { Name="Martin", Sex=true, Age=30 },
new { Name="Jerry", Sex=false, Age=24 },
new { Name="Brog", Sex=false, Age=25 },
new { Name="Vicky", Sex=true, Age=20 }
};
/* 执行简单Linq查询
* 检索所有年龄在24岁以内的人
* 查询结果放在results变量中
* results变量的类型与数组persons相同
*/
var results = from p in persons where p.Age <= 24 select p;
foreach (var person in results)
{
Console.WriteLine(person.Name);
}
// 定义匿名数组customers, 并为其赋初值
// 该数组是匿名类型的
var customers = new[]
{
new
{
Name = "Van's", City = "China", Orders = new[]
{
new { OrderNo = 0, OrderName = "C#", OrderDate = new DateTime(2007, 9, 5) },
new { OrderNo = 1, OrderName = "Head ", OrderDate = new DateTime(2007, 9, 15) },
new { OrderNo = 2, OrderName = "ASP", OrderDate = new DateTime(2007, 09, 18) },
new { OrderNo = 3, OrderName = "C++", OrderDate = new DateTime(2002, 9, 20) }
}
},
new
{
Name = "Brog", City = "China", Orders = new[]
{
new { OrderNo = 0, OrderName = "C#", OrderDate = new DateTime(2007, 9, 15) }
}
},
new
{
Name = "Vicky", City = "London", Orders = new[]
{
new { OrderNo = 0, OrderName = "C++", OrderDate = new DateTime(2007, 9, 20) }
}
}
};
/* 执行多重Linq查询
* 检索所在城市为中国, 且订单日期为2007年以后的所有记录
* 查询结果是一个匿名类型的数组
* 其中包含客户名, 订单号, 订单日期, 订单名四个字段
*/
var someCustomers = from c in customers where c.City == "China"
from o in c.Orders where o.OrderDate.Year >= 2007
select new { c.Name, o.OrderNo, o.OrderDate, o.OrderName };
foreach (var customer in someCustomers)
{
Console.WriteLine( customer.Name + ", "+customer.OrderNo+", " + customer.OrderName + ", " + customer.OrderDate.ToString("D") );
}