C# 3.0新特性

http://baike.baidu.com/view/1406676.htm(轉) 

微軟最近的動作頻繁,C#2.0規範纔剛推出不久,我還沒來的及完全消化C#2.0中的所有新特性,而今又推出了C#3.0,在短短几年中,微軟就對C#進行了兩次大的升級,爲C#加入了許多新的特性,從而使C#變得更加強大也更加現代了。下面我就把C#3.0中的新特性的要點列出來,以供大家快速瀏覽。
第一個特性:隱式類型化本地變量
  這個特性非常簡單,有些JavaScript的影子,我們可以統一使用使用"var"關鍵字來聲明局部變量,而不再需要指明變量的確切類型了,變量的確切類型可通過聲明變量時的初始值推斷出來。這樣一來,可以大大簡化我們聲明局部變量的工作量了,下面是一個例子:

class LocalVariables : AppRunner.AbstractApplication
{
 public override void Run()
 {
  var intValue = 5;
  var stringValue = "This is a string";
  var customClass = new LocalVariables();
  var intArray = new int[3] { 1, 2, 3 };
  foreach (var value in intArray)
   Console.WriteLine(value);            
 }
}

上面的代碼將被解析成:

class LocalVariables : AppRunner.AbstractApplication
{
 public override void Run()
 {
  int intValue = 5;
  string stringValue = "This is a string";
  LocalVariables customClass = new LocalVariables();
  int[] intArray = new int[3];
  foreach (int value in intArray)
   Console.WriteLine(value);            
 }
}

  要特別注意的是,由於變量的類型是通過變量初始值推斷而來的,所以在聲明變量的同時必需爲變量指定初始值。並且,變量並不是沒有類型的,變量一旦初始化之後,類型就確定下來了,以後就只能存儲某種類型的值了,比如上面的stringValue的類型經推斷爲string,所以該變量就只能保存string類型的值了。
第二個特性:匿名類型  
  有些時候我們需要臨時保存一些運算的中間結果,特別是當這些中間結果是由多個部份組成時,我們常常會去聲明一個新的類型,以方便保存這些中間結果。表面上看起來這很正常,而細想之後就會發現,這個新類型只服務於這個函數,其它地方都不會再使用它了,就爲這一個函數而去定義一個新的類型,確實有些麻煩。
  現在,C#3.0中的匿名類型特性就可以很好的解決上面提到的問題,通過匿名類型,我們可以簡單使用new { 屬性名1=值1, 屬性名2=值2, ..... , 屬性名n=值n }的形式直接在函數中創建新的類型,看下面這個例子:

class AnonymousType : AppRunner.AbstractApplication
{
 public override void Run()
 {
  var anonymousType1 = new {
   CardNumber = "10001", Name = "van’s", Sex = true
  };
  Console.WriteLine(anonymousType1.CardNumber);
  Console.WriteLine(anonymousType1.Name);
  var anonymousType2 = new {
   CardNumber = "10002", Name = "martin", Sex = true
  };
  anonymousType2 = anonymousType1;
 }
}

  在新類型中只能有字段成員,而且這些字段的類型也是通過初值的類型推斷出來的。如果在聲明新的匿名類型時,新類型的字段名、順序以及初始值的類型是一致的,那麼將會產生相同的匿名類型,所以上例中anonymousType1和anonymousType2的類型是相同的,自然能進行anonymousType2=anonymousType1的賦值。

第三個特性:隱式類型化數組
  這個特性是對隱式類型化本地變量的擴展,有了這個特性,將使我們創建數組的工作變得簡單。我們可以直接使用"new[]"關鍵字來聲明數組,後面跟上數組的初始值列表。在這裏,我們並沒有直接指定數組的類型,數組的類型是由初始化列表推斷出來的。

class AnonymousTypeArray : AppRunner.AbstractApplication
{
 public override void Run()
 {
  var intArray = new[] { 1, 2, 3, 4, 5 };
  var doubleArray = new[] { 3.14, 1.414 };
  var anonymousTypeArray = new[] {
   new { Name="van’s", Sex=false, Arg=22 },
   new { Name="martin", Sex=true, Arg=23 }
  };
   
  Console.WriteLine(intArray);
  Console.WriteLine(doubleArray);
  Console.WriteLine(anonymousTypeArray[0].Name);
 }
}

  上面的代碼中,anonymousTypeArray變量的聲明同時運用了隱式類型化數組和匿名類型兩種特性,首先創建匿名類型,然後再初始值列表,推斷出數組的確切類型。

第四個特性:對象構造者
  我們在聲明數組時,可以同時對其進行初始化,這樣就省去了很多麻煩,但是在創建類的對象時,這招可就不靈了,我們要麼調用該類的構造函數完成對象的初始化,要麼就手工進行初始化。這兩種方法都不太方便,使用構造函數來對對象進行初始化時,我們爲了某種靈活性,可能需要編寫構造函數的多個重載版本,實在是麻煩。
  C#3.0中加入的對象構造者特性,使得對象的初始化工作變得格外簡單,我們可以採用類似於數組初始化的方式來初始化類的對象,方法就是直接在創建類對象的表達式後面跟上類成員的初始化代碼。具體示例如下:

class Point
{
 public int X { get; set; }
 public int Y { get; set; }
 public override string ToString()
 {
  return "(" + X.ToString() + ", " + Y.ToString() + ")";
 }
}
  
class Rectangle
{
 public Point P1 { get; set; }
 public Point P2 { get; set; }
 public Rectangle()
 {
  P1 = new Point();
  P2 = new Point();
 }

 public override string ToString()
 {
  return "P1: " + P1 + ", P2: " + P2;
 }
}
class ObjectBuilder : AppRunner.AbstractApplication
{
 public override void Run()
 {
  Point thePoint = new Point() { X = 1, Y = 2 };
  Console.WriteLine("Point(X, Y) = ", thePoint);

  Rectangle theRectangle = new Rectangle() {  
   P1 = { X = 1, Y = 1 }, P2 = { X = 100, Y = 200 }  
  };
  Console.WriteLine(theRectangle);
 }
}

  我們在定義Point類的X和Y屬性時,只須寫上該屬性的get和set訪問器聲明,C#編譯器會自動爲我們生成默認的get和set操作代碼,當我們需要定義簡單屬性時,這個特性非常有用。
  我們以new Point() { X = 1, Y = 2 }語句,輕鬆的完成了對Point類的初始化工作。在創建類的對象時,我們可以按照需要去初始化類的對象,只要在類的創建表達式後跟上要初始化屬性的列表即可,且可以只對需要初始化的屬性賦初值,而無需把所有屬性的初始值都寫上去。
  在theRectangle對象的初始化表達式中,我們首先對P1屬性進行初始化,然而P1屬性也是一個自定義的類型,所以P1屬性的初始化是另一個類型(Point)的初始化表達式,我們可以這樣的方式來對更加複雜的類型進行初始化。
  上篇文章中介紹了C# 3.0中比較簡單的四個特性,分別是隱式類型化本地變量、匿名類型、隱式類型化數組,以及對象構造者,下面我將對C# 3.0中的較複雜,同時也是非常強大的幾個特性進行介紹,供大家快速瀏覽。
第五個特性:集合構造者
  我們可以在聲明數組的同時,爲其指定初始值,方法是直接在數組聲明的後面跟上初始值列表。這樣就使數組的初始化工作變得簡單,而對於我們自己創建的集合類型,就無法享受到與普通數組一樣的待遇了,我們無法在創建自定義集合對象的同時,使用數組的初始化語法爲其指定初始值。
  C# 3.0中加入的集合構造者特性,可使我們享受到與普通數組一樣的待遇,從而在創建集合對象的同時爲其指定初始值。爲了做到這一點,我們需要讓我們的集合實現ICollection<T>接口,在這個接口中,完成初始化操作的關鍵在於Add函數,當我使用初始化語法爲集合指定初始值時,C#編譯器將自動調用ICollection<T>中的Add函數將初始列表中的所有元素加入到集合中,以完成集合的初始化操作。使用示例如下:
class CollectionInitializer : AppRunner.AbstractApplication
{
class StringCollection : ICollection<string>
{
  public void Add(string item)
  {
   Console.WriteLine(item);
  }

  // Other ICollection<T> Members
}

public override void Run()
{
  StringCollection strings = new StringCollection() { "Van's", "Brog", "Vicky" };
}
}
  在這個示例中,編譯器會自動爲strings對象調用Add方法,以將初始值列表中的所有元素加入到集合中,這裏我們只是簡單將初始值列表中的元素輸出到控制檯。
第六個特性:Lambda表達式
  C# 2.0中加入的匿名代理,簡化了我們編寫事件處理函數的工作,使我們不再需要單獨聲明一個函數來與事件綁定,只需要使用delegate關鍵字在線編寫事件處理代碼。
  而C# 3.0則更進一步,通過Lambda表達式,我們可以一種更爲簡潔方式編寫事件處理代碼,新的Lambda事件處理代碼看上去就像一個計算表達式,它使用"=>"符號來連接事件參數和事件處理代碼。我可以這樣寫:SomeEvent += 事件參數 => 事件處理代碼;下面是完整的示例:
delegate T AddDelegate<T>(T a, T b);
class LambdaExpression : AppRunner.AbstractApplication
{
public static event EventHandler MyEvent;

public override void Run()
{
  MyEvent += delegate(object s, EventArgs e)
  {
   Console.WriteLine(s);
  };

  MyEvent += (s, e) => { Console.WriteLine(s); };
  
  MyEvent(this, null);
  
  AddDelegate<string> add = (a, b) => a + b;
  
  Console.WriteLine(add("Lambda", "Expression"));
}
}

  在上面的例子中,分別使用了匿名代理和Lambda表達式來實現同樣的功能,可以明顯看出Lambda表達式的實現更爲簡潔。我們在使用Lambda表達式編寫事件處理代碼時,無需指明事件參數的類型,且返回值就是最後一條語句的執行結果。
第七個特性:擴展方法
  當我們需要對已有類的功能進行擴展時,我們通常會想到繼承,繼承已有類,然後爲其加入新的行爲。而C# 3.0中加入的擴展方法特性,則提供了另一種實現功能擴展的方式,我們可以在不使用繼承的前提下實現對已有類本身的擴展,這種方法並不會產生新的類型,而是採用向已有類中加入新方法的方式來完成功能擴展。
  在對已有類進行擴展時,我們需將所有擴展方法都寫在一個靜態類中,這個靜態類就相當於存放擴展方法的容器,所有的擴展方法都可以寫在這裏面。而且擴展方法採用一種全新的聲明方式:public static 返回類型 擴展方法名(this 要擴展的類型 sourceObj [,擴展方法參數列表]),與普通方法聲明方式不同,擴展方法的第一個參數以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;
}
}
class ExtensionMethods : AppRunner.AbstractApplication
{
public override void Run()
{
  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的原因。下面看一個例子:
class LinqExpression : AppRunner.AbstractApplication
{
public override void Run()
{
  // 定義匿名數組persons, 併爲其賦初值
  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);
  }

  Console.WriteLine();


  // 定義匿名數組customers, 併爲其賦初值
  // 該數組是匿名類型的
  var customers = new[] {
   new {
    Name="Van's", City="China", Orders=new[] {
     new {
      OrderNo=0,
      OrderName="C# Programming Language(Second Edition)",
      OrderDate=new DateTime(2007,9, 5)
     },
     new {
      OrderNo=1,
      OrderName="Head First Design Patterns(Chinese Edition)",
      OrderDate=new DateTime(2007,9,15)
     },
     new {
      OrderNo=2,
      OrderName="ASP.NET Unleashed 2.0(Chinese Edition)",
      OrderDate=new DateTime(2007,09,18)
     },
     new {
      OrderNo=3,
      OrderName="The C++ Programming Langauge(Special Edition)",
      OrderDate=new DateTime(2002, 9, 20)
     }
    }
   },
   new {
    Name="Brog", City="China", Orders=new[] {
     new {
      OrderNo=0,
      OrderName="C# Programming Language(Second Edition)",
      OrderDate=new DateTime(2007, 9, 15)
     }
    }
   },
   new {
    Name="Vicky", City="London", Orders=new[] {
     new { OrderNo=0,
      OrderName="C++ Programming Language(Special Edition)",
      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.OrderName + ",  " +
    customer.OrderDate.ToString("D")
   );
  }
}
}
  從上面的例子中,我們可以看到Linq查詢的強大特性,它允許我們進行簡單查詢,或者進行更爲複雜的多重連接查詢。且查詢的結果還可以是自定義的匿名類型。
  以上是對C# 3.0中新增的八大特性的簡要介紹,如果想了解更爲深入的內容,可查看C# 3.0的官方規範。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章