C# 2.0, 3.0, 4.0 各版本的新特性


列個梗概,給自己看的。

C# 2.0

泛型(Generics)

泛型是CLR 2.0中引入的最重要的新特性,使得可以在類、方法中對使用的類型進行參數化。

例如,這里定義了一個泛型類:

class MyCollection<T> { T variable1; private void Add(T param){ } } 

使用的時候:MyCollection<string> list2 = new MyCollection<string>(); MyCollection<Object> list3 = new MyCollection<Object>();

 

 

泛型的好處

  • 編譯時就可以保證類型安全
  • 不用做類型裝換,獲得一定的性能提升

 

 

 

泛型方法、泛型委托、泛型接口

除了泛型類之外,還有泛型方法、泛型委托、泛型接口:

 
        

//泛型委托 public static delegate T1 MyDelegate<T1, T2>(T2 item); MyDelegate<Int32, String> MyFunc = new MyDelegate<Int32, String>(SomeMethd); //泛型接口 public class MyClass<T1, T2, T3> : MyInteface<T1, T2, T3> { public T1 Method1(T2 param1, T3 param2) { throw new NotImplementedException(); } } interface MyInteface<T1, T2, T3> { T1 Method1(T2 param1, T3 param2); }

 
        

//泛型方法 static void Swap<T>(ref T t1, ref T t2) { T temp = t1; t1 = t2; t2 = temp; } String str1 = "a"; String str2 = "b"; Swap<String>(ref str1, ref str2);

 
        
 
        

 

泛型約束(constraints)
可以給泛型的類型參數上加約束,可以要求這些類型參數滿足一定的條件

約束

說明

where T: struct 類型參數需是值類型
where T : class 類型參數需是引用類型
where T : new() 類型參數要有一個public的無參構造函數
where T : <base class name> 類型參數要派生自某個基類
where T : <interface name> 類型參數要實現了某個接口
where T : U 這里T和U都是類型參數,T必須是或者派生自U

 

這些約束,可以同時一起使用:

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new() { // ... }

 

default 關鍵字

這個關鍵可以使用在類型參數上:

default(T);

對於值類型,返回0,引用類型,返回null,對於結構類型,會返回一個成員值全部為0的結構實例。

 

 

迭代器(iterator)

可以在不實現IEnumerable就能使用foreach語句,在編譯器碰到yield return時,它會自動生成IEnumerable 接口的方法。在實現迭代器的方法或屬性中,返回類型必須是IEnumerable, IEnumerator, IEnumerable<T>,或 IEnumerator<T>。迭代器使得遍歷一些零碎數據的時候很方便,不用去實現Current, MoveNext 這些方法。

public System.Collections.IEnumerator GetEnumerator() { yield return -1; for (int i = 1; i < max; i++) { yield return i; } }

 

 

可空類型(Nullable Type)

可空類型System.Nullable<T>,可空類型僅針對於值類型,不能針對引用類型去創建。System.Nullable<T>簡寫為T ?。

int? num = null; if (num.HasValue == true) { System.Console.WriteLine("num = " + num.Value); } else { System.Console.WriteLine("num = Null"); }

如果HasValue為false,那么在使用value值的時候會拋出異常。把一個Nullable的變量x賦值給一個非Nullable的變量y可以這么寫:

int y = x ?? -1;

 

 

匿名方法(Anonymous Method)

在C#2.0之前,給只能用一個已經申明好的方法去創建一個委托。有了匿名方法后,可以在創建委托的時候直接傳一個代碼塊過去。

delegate void Del(int x); Del d = delegate(int k) { /* ... */ }; System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); } ); 委托語法的簡化// C# 1.0的寫法 ThreadStart ts1 = new ThreadStart(Method1); // C# 2.0可以這么寫 ThreadStart ts2 = Method1;

 
 
         
        

委托的協變和逆變(covariance and contravariance)

有下面的兩個類:

class Parent { } class Child: Parent { }

然后看下面的兩個委托:

public delegate Parent DelgParent(); public delegate Child DelgChild(); public static Parent Method1() { return null; } public static Child Method2() { return null; } static void Main() { DelgParent del1= Method1 DelgChild del2= Method2; del1 = del2; }

注意上面的,DelgParent 和DelgChild 是完全不同的類型,他們之間本身沒有任何的繼承關系,所以理論上來說他們是不能相互賦值的。但是因為協變的關系,使得我們可以把DelgChild類型的委托賦值給DelgParent 類型的委托。協變針對委托的返回值,逆變針對參數,原理是一樣的。

 

 

部分類(partial)

在申明一個類、結構或者接口的時候,用partial關鍵字,可以讓源代碼分布在不同的文件中。我覺得這個東西完全是為了照顧Asp.net代碼分離而引入的功能,真沒什么太大的實際用處。微軟說在一些大工程中可以把類分開在不同的文件中讓不同的人去實現,方便團隊協作,這個我覺得純屬胡扯。

部分類僅是編譯器提供的功能,在編譯的時候會把partial關鍵字定義的類和在一起去編譯,和CRL沒什么關系。

 

 

靜態類(static class)

靜態類就一個只能有靜態成員的類,用static關鍵字對類進行標示,靜態類不能被實例化。靜態類理論上相當於一個只有靜態成員並且構造函數為私有的普通類,靜態類相對來說的好處就是,編譯器能夠保證靜態類不會添加任何非靜態成員。

 

 

global::

這個代表了全局命名空間(最上層的命名空間),也就是任何一個程序的默認命名空間。

class TestApp { public class System { } const int Console = 7; static void Main() { //用這個訪問就會出錯,System和Console都被占用了 //Console.WriteLine(number); global::System.Console.WriteLine(number); } }

 

 

extern alias

用來消除不同程序集中類名重復的沖突,這樣可以引用同一個程序集的不同版本,也就是說在編譯的時候,提供了一個將有沖突的程序集進行區分的手段。

在編譯的時候,使用命令行參數來指明alias,例如:

/r:aliasName=assembly1.dll

在Visual Studio里面,在被引用的程序集的屬性里面可以指定Alias的值,默認是global。

然后在代碼里面就可以使用了:

extern alias aliasName; //這行需要在using這些語句的前面 using System; using System.Collections.Generic; using System.Text; using aliasName.XXX;

 

屬性Accessor訪問控制

public virtual int TestProperty { protected set { } get { return 0; } }

 

 

友元程序集(Friend Assembly)

可以讓其它程序集訪問自己的internal成員(private的還是不行),使用Attributes來實現,例如:

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

注意這個作用范圍是整個程序集。

 

 

fixed關鍵字

可以使用fixed關鍵字來創建固定長度的數組,但是數組只能是bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float, double中的一種。

這主要是為了更好的處理一些非托管的代碼。比如下面的這個結構體:

public struct MyArray { public fixed char pathName[128]; }

如果不用fixed的話,無法預先占住128個char的空間,使用fixed后可以很好的和非托管代碼進行交互。

 

 

volatile關鍵字

用來表示相關的字可能被多個線程同時訪問,編譯器不會對相應的值做針對單線程下的優化,保證相關的值在任何時候訪問都是最新的。

 

 

#pragma warning

用來取消或者添加編譯時的警告信息。每個警告信息都會有個編號,如果warning CS01016之類的,使用的時候取CS后面的那個數字,例如:

#pragma warning disable 414, 3021

這樣CS414和CS3021的警告信息就都不會顯示了。

 

 

 

 

 

C# 3.0

 

類型推斷

申明變量的時候,可以不用直指定類型:

var i = 5;
var s = "Hello";
//兩種寫法是一樣的
int i = 5;
string s = "Hello";

類型推斷也支持數組:

var b = new[] { 1, 1.5, 2, 2.5 };            // double[]
var c = new[] { "hello", null, "world” };      // string[]

 

擴展方法

擴展方法必須被定義在靜態類中,並且必須是非泛型、非嵌套的靜態類。例如:

public static class JeffClass
{
    public static int StrToInt32(this string s)
    {
        return Int32.Parse(s);
    }

    public static T[] SomeMethd<T>(this T[] source, int pram1, int pram2)
    {
        /**/
    }
}

上面一個是給string類型的對象添加了一個方法,另一個是給所有類型的數組添加了一個方法,方法有兩個整型參數。

擴展方法只在當前的命名空間類有效,如果所在命名空間被其它命名空間import引用了,那么在其它命名空間中也有效。擴展方法的優先級低於其它的常規方法,也就是說如果擴展方法與其它的方法相同,那么擴展方法不會被調用。

 

λ表達式

λ表達式可以看成是對匿名方法的一個語法上的簡化,但是λ表達式同時可以裝換為表達式樹類型。

 

對象和集合的初始化

var contacts = new List<Contact> {
   new Contact {
      Name = "Chris",
      PhoneNumbers = { "123455", "6688" }
   },
   new Contact {
      Name = "Jeffrey",
      PhoneNumbers = { "112233" }
   }
};
 

匿名類型

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

 

自動屬性

會自動生成一個后台的私有變量

public Class Point
{
   public int X { get; set; }
   public int Y { get; set; }
}

 

查詢表達式

這個其實就是擴展方法的運用,編譯器提供了相關的語法便利,下面兩端代碼是等價的:

from g in
   from c in customers
   group c by c.Country
select new { Country = g.Key, CustCount = g.Count() }

customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

 

表達式樹

Func<int,int> f = x => x + 1;
Expression<Func<int,int>> e = x => x + 1; 

 

 

 

C# 4.0

協變和逆變

這個在C#2.0中就已經支持委托的協變和逆變了,C#4.0開始支持針對泛型接口的協變和逆變:

IList<string> strings = new List<string>();

IList<object> objects = strings;

協變和逆變僅針對引用類型。

 

動態綁定

看例子:

class BaseClass
{
    public void print()
    {
        Console.WriteLine();
    }
}
Object o = new BaseClass();
dynamic a = o;
//這里可以調用print方法,在運行時a會知道自己是個什么類型。 這里的缺點在於編譯的時候無法檢查方法的合法性,寫錯的話就會出運行時錯誤。
a.print();
 
 

可選參數,命名參數

private void CreateNewStudent(string name, int studentid = 0, int year = 1)

這樣,最后一個參數不給的話默認值就是1,提供這個特性可以免去寫一些重載方法的麻煩。

調用方法的時候,可以指定參數的名字來給值,不用按照方法參數的順序來制定參數值:

CreateNewStudent(year:2, name:"Hima", studentid: 4); //沒有按照方法定義的參數順序


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM