c#3.0新特性

介绍了C#3.0中的八大新特性,包括隐式类型化本地变量、匿名类型、隐式类型化数组等,重点讲解了Linq查询表达式的强大功能。
第一个特性:隐式类型化本地变量

  这个特性非常简单,有些JavaScript的影子,我们可以统一使用使用"var"关键字来声明局部变量,而不再需要指明变量的确切类型了,变量的确切类型可通过声明变量时的初始值推断出来。这样一来,可以大大简化我们声明局部变量的工作量了,下面是一个例子:

   1:      class LocalVariables : AppRunner.AbstractApplication
   
2:      {
   
3:          public override void Run()
   
4:          {
   
5:              var intValue = 5;
   
6:              var stringValue = "This is a string";
   
7:              var customClass = new LocalVariables();
   
8:              var intArray = new int[3] { 123 };
   
9:  
  
10:              foreach (var value in intArray)
  
11:                  Console.WriteLine(value);           
  
12:          }
  
13:      }
上面的代码将被解析成:
   
1:      class LocalVariables : AppRunner.AbstractApplication
   
2:      {
   
3:          public override void Run()
   
4:          {
   
5:              int intValue = 5;
   
6:              string stringValue = "This is a string";
   
7:              LocalVariables customClass = new LocalVariables();
   
8:              int[] intArray = new int[3];
   
9:  
  
10:              foreach (int value in intArray)
  
11:                  Console.WriteLine(value);           
  
12:          }
  
13:      }

  要特别注意的是,由于变量的类型是通过变量初始值推断而来的,所以在声明变量的同时必需为变量指定初始值。并且,变量并不是没有类型的,变量一旦初始化之后,类型就确定下来了,以后就只能存储某种类型的值了,比如上面的stringValue的类型经推断为string,所以该变量就只能保存string类型的值了。


第二个特性:匿名类型 

  有些时候我们需要临时保存一些运算的中间结果,特别是当这些中间结果是由多个部份组成时,我们常常会去声明一个 新的类型,以方便保存这些中间结果。表面上看起来这很正常,而细想之后就会发现,这个新类型只服务于这个函数,其它地方都不会再使用它了,就为这一个函数 而去定义一个新的类型,确实有些麻烦。

  现在,C#3.0中的匿名类型特性就可以很好的解决上面提到的问题,通过匿名类型,我们可以简单使用new { 属性名1=值1, 属性名2=值2, ..... , 属性名n=值n }的形式直接在函数中创建新的类型,看下面这个例子:

   1:      class AnonymousType : AppRunner.AbstractApplication
   
2:      {
   
3:          public override void Run()
   
4:          {
   
5:              var anonymousType1 = new {
   6:                  CardNumber = "10001", Name = "van's", Sex = true
   
7:              };
   
8:  
   
9:              Console.WriteLine(anonymousType1.CardNumber);
  
10:              Console.WriteLine(anonymousType1.Name);
  
11:  
  
12:              var anonymousType2 = new {
  
13:                  CardNumber = "10002", Name = "martin", Sex = true
  
14:              };
  
15:  
  
16:              anonymousType2 = anonymousType1;
  
17:          }
  
18:      }

 

  在新类型中只能有字段成员,而且这些字段的类型也是通过初值的类型推断出来的。如果在声明新的匿名类型时,新类 型的字段名、顺序以及初始值的类型是一致的,那么将会产生相同的匿名类型,所以上例中anonymousType1和anonymousType2的类型 是相同的,自然能进行anonymousType2=anonymousType1的赋值。


第三个特性:隐式类型化数组

  这个特性是对隐式类型化本地变量的扩展,有了这个特性,将使我们创建数组的工作变得简单。我们可以直接使用"new[]"关键字来声明数组,后面跟上数组的初始值列表。在这里,我们并没有直接指定数组的类型,数组的类型是由初始化列表推断出来的。

   1:      class AnonymousTypeArray : AppRunner.AbstractApplication
   
2:      {
   
3:          public override void Run()
   
4:          {
   
5:              var intArray = new[] { 12345 };
   
6:              var doubleArray = new[] { 3.141.414 };
   
7:              var anonymousTypeArray = new[] {
   
8:                  new { Name="van's", Sex=false, Arg=22 },
   
9:                  new { Name="martin", Sex=true, Arg=23 }
  
10:              };
  
11:  
  
12:              Console.WriteLine(intArray);
  
13:              Console.WriteLine(doubleArray);
  
14:              Console.WriteLine(anonymousTypeArray[0].Name);
  
15:          }
  
16:      }

 

  上面的代码中,anonymousTypeArray变量的声明同时运用了隐式类型化数组和匿名类型两种特性,首先创建匿名类型,然后再初始值列表,推断出数组的确切类型。


第四个特性:对象构造者

  我们在声明数组时,可以同时对其进行初始化,这样就省去了很多麻烦, 但是在创建类的对象时,这招可就不灵了,我们要么调用该类的构造函数完成对象的初始化,要么就手工进行初始化。这两种方法都不太方便,使用构造函数来对对 象进行初始化时,我们为了某种灵活性,可能需要编写构造函数的多个重载版本,实在是麻烦。

  C#3.0中加入的对象构造者特性,使得对象的初始化工作变得格外简单,我们可以采用类似于数组初始化的方式来初始化类的对象,方法就是直接在创建类对象的表达式后面跟上类成员的初始化代码。具体示例如下:

   1:     class Point
   
2:     {
   
3:          public int X { getset; }
   
4:          public int Y { getset; }
   
5:  
   
6:          public override string ToString()
   
7:          {
   
8:              return "(" + X.ToString() + "" + Y.ToString() + ")";
   
9:          }
  
10:      }
  
11:  
  
12:      class Rectangle
  
13:      {
  
14:          public Point P1 { getset; }
  
15:          public Point P2 { getset; }
  
16:  
 
17:          public Rectangle()
  
18:          {
  
19:              P1 = new Point();
  
20:              P2 = new Point();
  
21:          }
  
22:  
  
23:          public override string ToString()
  
24:          {
  
25:              return "P1: " + P1 + ", P2: " + P2;
  
26:          }
  
27:      }
  
28:  
  
29:      class ObjectBuilder : AppRunner.AbstractApplication
  
30:      {
  
31:          public override void Run()
  
32:          {
  
33:              Point thePoint = new Point() { X = 1, Y = 2 };
  
34:              Console.WriteLine("Point(X, Y) = {0}", thePoint);
  
35:  
  
36:              Rectangle theRectangle = new Rectangle() {
  
37:                  P1 = { X = 1, Y = 1 }, P2 = { X = 100, Y = 200 }
  
38:              };
  
39:              Console.WriteLine(theRectangle);
  
30:          }
  
41:      }

 

  我们在定义Point类的X和Y属性时,只须写上该属性的get和set访问器声明,C#编译器会自动为我们生成默认的get和set操作代码,当我们需要定义简单属性时,这个特性非常有用。

  我们以new Point() { X = 1, Y = 2 }语句,轻松的完成了对Point类的初始化工作。在创建类的对象时,我们可以按照需要去初始化类的对象,只要在类的创建表达式后跟上要初始化属性的列表即可,且可以只对需要初始化的属性赋初值,而无需把所有属性的初始值都写上去。

  在theRectangle对象的初始化表达式中,我们首先对P1属性进行初始化,然而P1属性也是一个自定义的类型,所以P1属性的初始化是另一个类型(Point)的初始化表达式,我们可以这样的方式来对更加复杂的类型进行初始化。

第五个特性:集合构造者

  我们可以在声明数组的同时,为其指定初始值,方法是直接在数组声明的后面跟上初始值列表。这样就使数组的初始化工作变得简单,而对于我们自己创建的集合类型,就无法享受到与普通数组一样的待遇了,我们无法在创建自定义集合对象的同时,使用数组的初始化语法为其指定初始值。

  C# 3.0中加入的集合构造者特性,可使我们享受到与普通数组一样的待遇,从而在创建集合对象的同时为其指定初始值。为了做到这一点,我们需要让我们的集合实现ICollection<T>接口,在这个接口中,完成初始化操作的关键在于Add函数,当我使用初始化语法为集合指定初始值时,C#编译器将自动调用ICollection<T>中的Add函数将初始列表中的所有元素加入到集合中,以完成集合的初始化操作。使用示例如下:

   1:      class CollectionInitializer : AppRunner.AbstractApplication
   
2:      {
   
3:          class StringCollection : ICollection<string>
   
4:          {
   
5:              public void Add(string item)
   
6:              {
   
7:                  Console.WriteLine(item);
   
8:              }
   
9:  
  
10:              // Other ICollection<T> Members
  
11:          }
  
12:  
  
13:          public override void Run()
  
14:          {
  
15:              StringCollection strings = new StringCollection() { "Van's""Brog""Vicky" };
  
16:          }
  
17:      }


  在这个示例中,编译器会自动为strings对象调用Add方法,以将初始值列表中的所有元素加入到集合中,这里我们只是简单将初始值列表中的元素输出到控制台。


第六个特性:Lambda表达式

  C# 2.0中加入的匿名代理,简化了我们编写事件处理函数的工作,使我们不再需要单独声明一个函数来与事件绑定,只需要使用delegate关键字在线编写事件处理代码。

  而C# 3.0则更进一步,通过Lambda表达式,我们可以一种更为简洁方式编写事件处理代码,新的Lambda事件处理代码看上去就像一个计算表达式,它使用"=>"符号来连接事件参数和事件处理代码。我可以这样写:SomeEvent += 事件参数 => 事件处理代码;下面是完整的示例:

   1:      delegate T AddDelegate<T>(T a, T b);
   
2:  
   
3:      class LambdaExpression : AppRunner.AbstractApplication
   
4:      {
   
5:          public static event EventHandler MyEvent;
   
6:  
   
7:          public override void Run()
   
8:          {
   
9:              MyEvent += delegate(object s, EventArgs e)
  
10:              {
  
11:                  Console.WriteLine(s);
  
12:              };
  
13:  
  
14:              MyEvent += (s, e) => { Console.WriteLine(s); };
  
15:             
  
16:              MyEvent(thisnull);
  
17:  
  
18:              AddDelegate<string> add = (a, b) => a + b;
  
19:  
  
20:              Console.WriteLine(add("Lambda""Expression"));
  
21:          }
  
22:      }

 

  在上面的例子中,分别使用了匿名代理和Lambda表达式来实现同样的功能,可以明显看出Lambda表达式的实现更为简洁。我们在使用Lambda表达式编写事件处理代码时,无需指明事件参数的类型,且返回值就是最后一条语句的执行结果。


第七个特性:扩展方法

  当我们需要对已有类的功能进行扩展时,我们通常会想到继承,继承已有类,然后为其加入新的行为。而C# 3.0中加入的扩展方法特性,则提供了另一种实现功能扩展的方式,我们可以在不使用继承的前提下实现对已有类本身的扩展,这种方法并不会产生新的类型,而是采用向已有类中加入新方法的方式来完成功能扩展。

  在对已有类进行扩展时,我们需将所有扩展方法都写在一个静态类中,这个静态类就相当于存放扩展方法的容器,所有的扩展方法都可以写在这里面。而且扩展方法采用一种全新的声明方式:public static 返回类型 扩展方法名(this 要扩展的类型 sourceObj [,扩展方法参数列表]),与普通方法声明方式不同,扩展方法的第一个参数以this关键字开始,后跟被扩展的类型名,然后才是真正的参数列表。下面是使用示例:

   1:      static class Extensions
   
2:      {
   
3:          public static int ToInt32(this string source)
   
4:          {
   
5:              return Int32.Parse(source);
   
6:          }
   
7:  
   
8:          public static T[] Slice<T>(this T[] source, int index, int count)
   
9:          {
  
10:              if (index < 0 || count < 0 || index + count > source.Length)
  
11:              {
  
12:                  throw new ArgumentException();
  
13:              }
  
14:  
  
15:              T[] result = new T[count];
  
16:              Array.Copy(source, index, result, 0, count);   
  
17:              return result;
  
18:          }
  
19:      }
  
20:  
  
21:      class ExtensionMethods : AppRunner.AbstractApplication
  
22:      {
  
23:          public override void Run()
  
24:          {
  
25:              string number = "123";
  
26:              Console.WriteLine(number.ToInt32());
  
27:  
  
28:              int[] intArray = new int[] { 123 };
  
29:  
  
30:              intArray = intArray.Slice(12);
  
31:  
  
32:              foreach (var i in intArray)
  
33:                  Console.WriteLine(i);
  
34:          }
  
35:      }

 

  在上面的示例中,静态的Extensions类中有两个扩展方法,第 一个方法是对string类的扩展,它为string类加入了名为ToInt32的方法,该方法没有参数,并返回一个int类型的值,它将完成数字字符向 整数的转换。有了这个扩展方法之后,就可对任意string类的对象调用ToInt32方法了,该方法就像其本身定义的一样。

  第二个扩展方法是一个范型方法,它是对所有数组类型的扩展,该方法完成数组的切片操作。

  C# 3.0中的Linq表达式,就是大量运用扩展方法来实现数据查询的。


第八个特性:Linq查询表达式

  C# 3.0中加入的最为复杂的特性就是Linq查询表达式了,这使我们可直接采用类似于SQL的语法对集合进行查询,这就使我们可以享受到关系数据查询的强大功能。

  Linq查询表达式是建立在多种C# 3.0的新特性之上的,这也是我为什么最后才介绍Linq的原因。下面看一个例子:

   1:      class LinqExpression : AppRunner.AbstractApplication
   
2:      {
   
3:          public override void Run()
   
4:          {
   
5:              // 定义匿名数组persons, 并为其赋初值
   
6:              var persons = new[] {
   
7:                  new { Name="Van's", Sex=false, Age=22 },
   
8:                  new { Name="Martin", Sex=true, Age=30 },
   
9:                  new { Name="Jerry", Sex=false, Age=24 },
  
10:                  new { Name="Brog", Sex=false, Age=25 },
  
11:                  new { Name="Vicky", Sex=true, Age=20 }
  
12:              };
  
13:  
  
14:  
  
15:              /*
  16:                  执行简单Linq查询
  17:  
  18:                  检索所有年龄在24岁以内的人
  19:                  查询结果放在results变量中
  20: 
  21:                  results变量的类型与数组persons相同               
  22:              
*/
  
23:              var results = from p in persons
  
24:                           where p.Age <= 24
  
25:                            select p;
  
26:  
  
27:              foreach (var person in results)
  
28:              {
  
29:                  Console.WriteLine(person.Name);
  
30:              }
  
31
  
32:              Console.WriteLine();
  
33:  
  
34:  
  
35:              // 定义匿名数组customers, 并为其赋初值
  
36:              // 该数组是匿名类型的
  37:              var customers = new[] {
 
38:                  new {
 
39:                      Name="Van's", City="China", Orders=new[] {
 
40:                          new {
 
41:                              OrderNo=0,
 
42:                              OrderName="C# Programming Language(Second Edition)",
 
43:                              OrderDate=new DateTime(2007,95)
 
44:                          },
 
45:                          new {
 
46:                              OrderNo=1,
 
47:                              OrderName="Head First Design Patterns(Chinese Edition)",
 
48:                              OrderDate=new DateTime(2007,9,15)
 
49:                          },
 
50:                          new {
 
51:                              OrderNo=2,
 
52:                              OrderName="ASP.NET Unleashed 2.0(Chinese Edition)",
 
53:                              OrderDate=new DateTime(2007,09,18)
 
54:                          },
 
55:                          new {
 
56:                              OrderNo=3,
 
57:                              OrderName="The C++ Programming Langauge(Special Edition)",
 
58:                              OrderDate=new DateTime(2002920)
 
59:                          }
 
60:                      }
 
61:                  },
 
62:                  new {
 
63:                      Name="Brog", City="China", Orders=new[] {
 
64:                          new {
 
65:                              OrderNo=0,
 
66:                              OrderName="C# Programming Language(Second Edition)",
 
67:                              OrderDate=new DateTime(2007915)
 
68:                          }
 
69:                      }
 
70:                  },
 
71:                  new {
 
72:                      Name="Vicky", City="London", Orders=new[] {
 
73:                          new { OrderNo=0,
 
74:                              OrderName="C++ Programming Language(Special Edition)",
 
75:                              OrderDate=new DateTime(2007920)
 
76:                          }
 
77:                      }
 
78:                  }
 
79:              };
 
80:   
 
81:   
 
82:              /*
  83:                  执行多重Linq查询
  84:   
  85:                  检索所在城市为中国, 且订单日期为2007年以后的所有记录
  86:                  查询结果是一个匿名类型的数组
  87:                  其中包含客户名, 订单号, 订单日期, 订单名四个字段
  88:              
*/
 
89:              var someCustomers = from c in customers
 
90:                                  where c.City == "China"
 
91:                                  from o in c.Orders
 
92:                                  where o.OrderDate.Year >= 2007
 
93:                                  select new { c.Name, o.OrderNo, o.OrderDate, o.OrderName };
 
94:   
 
95:   
 
96:              foreach (var customer in someCustomers)
 
97:              {
 
98:                  Console.WriteLine(
 
99:                      customer.Name + "" + customer.OrderName + ",  " +
 
100:                      customer.OrderDate.ToString("D")
 
101:                  );
 
102:              }
 
103:          }
 
104:      }

   从上面的例子中,我们可以看到Linq查询的强大特性,它允许我们进行简单查询,或者进行更为复杂的多重连接查询。且查询的结果还可以是自定义的匿名类型。

  以上是对C# 3.0中新增的八大特性的简要介绍,如果想了解更为深入的内容,可查看C# 3.0的官方规范。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值