日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

带你了解C#每个版本新特性

發(fā)布時(shí)間:2023/12/4 C# 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 带你了解C#每个版本新特性 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上學(xué)時(shí)學(xué)習(xí)C#和.NET,當(dāng)時(shí)網(wǎng)上的資源不像現(xiàn)在這樣豐富,所以去電腦城買了張盜版的VS2005的光盤,安裝時(shí)才發(fā)現(xiàn)是VS2003,當(dāng)時(shí)有一種被坑的感覺,但也正是如此,讓我有了一個完整的.NET的學(xué)習(xí)生涯。

一直都認(rèn)為學(xué)習(xí)語言應(yīng)該系統(tǒng)的進(jìn)行學(xué)習(xí),了解每一個版本的新增特性,才能在實(shí)際應(yīng)用中做到有的放矢。最近發(fā)現(xiàn)團(tuán)隊(duì)中有不少人雖然用著最新的技術(shù),但知識儲備還停留在一個比較初始的狀態(tài),這樣在編碼過程中會走不少彎路。

本文梳理下C#從1.0到7.0版本的一些常用特性,對于不常用的或者我沒有用到過的一些特性,會列出來,但不會做詳細(xì)描述。另外C#8.0現(xiàn)在還沒有正式推出,并且目前我們也只是在使用dotNet Core2.1,所以C#8.0本文也不會涉及。

C#1.X

C#VS版本CLR版本.NET Framework
1.0VS20021.01.0
1.1VS20031.11.1

在C#1.0或1.1版本中,從語言的角度就是基本的面向?qū)ο蟮恼Z法,可以說任何一本C#語言的書籍都包含了C#1.X的所有內(nèi)容。

如果您已經(jīng)在使用C#語言編寫代碼,那么C#1.X的相關(guān)知識應(yīng)該已經(jīng)掌握。基礎(chǔ)語法部分這里就不再贅述了。

C#2.0

C#VS版本CLR版本.NET Framework
2.0VS20052.02.0

2.0中對應(yīng)VS2005我用的也不多,因?yàn)楹芸炀捅籚S2008替代了,不過在語言方面卻帶來了很多新的東西。

泛型

C#2中最重要的一個特性應(yīng)該就是泛型。泛型的用處就是在一些場景下可以減少強(qiáng)制轉(zhuǎn)換來提高性能。在C#1中就有很多的強(qiáng)制轉(zhuǎn)換,特別是對一些集合進(jìn)行遍歷時(shí),如ArrayList、HashTable,因?yàn)樗麄兪菫椴煌瑪?shù)據(jù)類型設(shè)計(jì)的集合,所以他們中鍵和值的類型都是object,這就意味著會平凡發(fā)生裝箱拆箱的操作。C#2中有了泛型,所以我們可以使用List、Dictionary。泛型能夠帶來很好的編譯時(shí)類型檢查,也不會有裝箱拆箱的操作,因?yàn)轭愋褪窃谑褂梅盒偷臅r(shí)候就已經(jīng)指定了。

.NET已經(jīng)通過了很多的泛型類型供我們使用,如上面提到的List,Dictionary,我們也可以自己來創(chuàng)建泛型類型(類、接口、委托、結(jié)構(gòu))或是方法。在定義泛型類型或時(shí)可以通過定義泛型約束來對泛型參數(shù)進(jìn)行限制,更好的使用編譯時(shí)檢查。泛型約束是通過關(guān)鍵字where來實(shí)現(xiàn)的,C#2中的泛型約束有4種:

分部類(Partil)

分部類可以允許我們在多個文件中為一個類型(class、struct、interface)編寫代碼,在Asp.Net2.0中用的極為廣泛。新建一個Aspx頁面,頁面的CodeBehind和頁面中的控件的定義就是通過分部類來實(shí)現(xiàn)的。如下:

public?partial?class?_Default?:?System.Web.UI.Page?public?partial?class?_Default?class?_Default?:?System.Web.UI.Page?
public?partial?class?_Default?

分部類使用關(guān)鍵字partial來定義,當(dāng)一個類中的代碼非常多時(shí),可以使用分部類來進(jìn)行拆分,這對代碼的閱讀很有好處,而且不會影響調(diào)用。不過現(xiàn)在我們前后端分離,后端代碼要做到單一職責(zé)原則,不會有很多大的類,所以這個特性很少用到。

靜態(tài)類

靜態(tài)類中的公用方法必須也是靜態(tài)的,可以由類名直接調(diào)用,不需要實(shí)例化,比較適用于編寫一些工具類。如System.Math類就是靜態(tài)類。工具類有一些特點(diǎn),如:所有成員都是靜態(tài)的、不需要被繼承、不需要進(jìn)行實(shí)例化。在C#1中我們可以通過如下代碼來實(shí)現(xiàn):

//聲明為密封類防止被繼承?public?sealed?class?StringHelper{????//添加私有無參構(gòu)造函ˉ數(shù)防止被實(shí)例化,如果不添加私有構(gòu)造函數(shù)?????//會自動生成共有無參構(gòu)造函數(shù)?????private?StringHelper(){};????public?static?int?StringToInt32(string?input)????{????????int?result=0;????????Int32.TryParse(input,?out?result);????????return?result;????}}
public?sealed?class?StringHelper
{
????//添加私有無參構(gòu)造函ˉ數(shù)防止被實(shí)例化,如果不添加私有構(gòu)造函數(shù)?
????//會自動生成共有無參構(gòu)造函數(shù)?
????private?StringHelper(){};
????public?static?int?StringToInt32(string?input)
????
{
????????int?result=0;
????????Int32.TryParse(input,?out?result);
????????return?result;
????}
}

C#2中可以使用靜態(tài)類來實(shí)現(xiàn):

public?static?class?StringHelper{????public?static?int?StringToInt32(string?input)????{????????int?result=0;????????Int32.TryParse(input,?out?result);????????return?result;????}}static?class?StringHelper
{
????public?static?int?StringToInt32(string?input)
????
{
????????int?result=0;
????????Int32.TryParse(input,?out?result);
????????return?result;
????}
}

屬性的訪問級別

在C#1中聲明屬性,屬性中的get和set的訪問級別是和屬性一致,要么都是public要么都是private,如果要實(shí)現(xiàn)get和set有不同的訪問級別,則需要用一種變通的方式,自己寫GetXXX和SetXXX方法。在C#2中可以單獨(dú)設(shè)置get和set的訪問級別,如下:

private?string?_name;public?string?Name{????get?{?return?_name;?}????private?set?{?_name?=?value;?}}string?_name;
public?string?Name
{
????get?{?return?_name;?}
????private?set?{?_name?=?value;?}
}

需要注意的是,不能講屬性設(shè)置為私有的,而將其中的get或是set設(shè)置成公有的,也不能給set和get設(shè)置相同的訪問級別,當(dāng)set和get的訪問級別相同時(shí),我們可以直接設(shè)置在屬性上。

命名空間別名

命名空間可以用來組織類,當(dāng)不同的命名空間中有相同的類時(shí),可以使用完全限定名來防止類名的沖突,C#1中可以使用空間別名來簡化書寫,空間別名用using關(guān)鍵字實(shí)現(xiàn)。但還有一些特殊情況,使用using并不能完全解決,所以C#2中提供了下面幾種特性:

我們在構(gòu)建命名空間和類的時(shí)候,盡量避免出現(xiàn)沖突的情況,這個特性也較少用到。

友元程序集

當(dāng)我們希望一個程序集中的類型可以被外部的某些程序集訪問,這時(shí)如果設(shè)置成Public,就可以被所有的外部程序集訪問。怎樣只讓部分程序集訪問,就要使用友元程序集了,具體參考之前的博文《C#:友元程序集(http://blog.fwhyy.com/2010/11/csharp-a-friend-assembly/)》

可空類型

可空類型就是允許值類型的值為null。通常值類型的值是不應(yīng)該為null的,但我們很多應(yīng)用是和數(shù)據(jù)庫打交道的,而數(shù)據(jù)庫中的類型都是可以為null值的,這就造成了我們寫程序的時(shí)候有時(shí)需要將值類型設(shè)置為null。在C#1中通常使用”魔值“來處理這種情況,比如DateTiem.MinValue、Int32.MinValue。在ADO.NET中所有類型的空值可以用DBNull.Value來表示。C#2中可空類型主要是使用System.Nullable的泛型類型,類型參數(shù)T有值類型約束。可以像下面這樣來定義可空類型:

Nullable<int>?i?=?20;Nullable<bool>?b?=?true;20;
Nullable<bool>?b?=?true;

C#2中也提供了更方便的定義方式,使用操作符?:

int??i?=?20;bool??b?=?true;20;
bool??b?=?true;

迭代器

C#2中對迭代器提供了更便捷的實(shí)現(xiàn)方式。提到迭代器,有兩個概念需要了解

看下面一個例子:

public?class?Test?{????static?void?Main()????{????????Person?arrPerson?=?new?Person("oec2003","oec2004","oec2005");????????foreach?(string?p?in?arrPerson)????????{????????????Console.WriteLine(p);????????}????????Console.ReadLine();????}}public?class?Person:IEnumerable?{????public?Person(params?string[]?names)????{????????_names?=?new?string[names.Length];????????names.CopyTo(_names,?0);????}????public?string[]?_names;????public?IEnumerator?GetEnumerator()????{????????return?new?PersonEnumerator(this);????}????private?string?this[int?index]????{????????get?{?return?_names[index];?}????????set?{?_names[index]?=?value;?}????}}public?class?PersonEnumerator?:?IEnumerator?{????private?int?_index?=?-1;????private?Person?_p;????public?PersonEnumerator(Person?p)?{?_p?=?p;?}????public?object?Current????{????????get?{?return?_p._names[_index];?}????}????public?bool?MoveNext()????{????????_index++;????????return?_index?<?_p._names.Length;????}????public?void?Reset()????{????????_index?=?-1;????}}class?Test?
{
????static?void?Main()
????
{
????????Person?arrPerson?=?new?Person("oec2003","oec2004","oec2005");
????????foreach?(string?p?in?arrPerson)
????????{
????????????Console.WriteLine(p);
????????}
????????Console.ReadLine();
????}
}
public?class?Person:IEnumerable?
{
????public?Person(params?string[]?names)
????
{
????????_names?=?new?string[names.Length];
????????names.CopyTo(_names,?0);
????}
????public?string[]?_names;
????public?IEnumerator?GetEnumerator()
????
{
????????return?new?PersonEnumerator(this);
????}
????private?string?this[int?index]
????{
????????get?{?return?_names[index];?}
????????set?{?_names[index]?=?value;?}
????}
}
public?class?PersonEnumerator?:?IEnumerator?
{
????private?int?_index?=?-1;
????private?Person?_p;
????public?PersonEnumerator(Person?p)?{?_p?=?p;?}
????public?object?Current
????{
????????get?{?return?_p._names[_index];?}
????}
????public?bool?MoveNext()
????
{
????????_index++;
????????return?_index?<?_p._names.Length;
????}
????public?void?Reset()
????
{
????????_index?=?-1;
????}
}

C#2中的迭代器變得非常便捷,使用關(guān)鍵字yield return關(guān)鍵字實(shí)現(xiàn),下面是C#2中使用yield return的重寫版本:

public?class?Test?{????static?void?Main()????{????????Person?arrPerson?=?new?Person("oec2003","oec2004","oec2005");????????foreach?(string?p?in?arrPerson)????????{????????????Console.WriteLine(p);????????}????????Console.ReadLine();????}}public?class?Person:IEnumerable?{????public?Person(params?string[]?names)????{????????_names?=?new?string[names.Length];????????names.CopyTo(_names,?0);????}????public?string[]?_names;????public?IEnumerator?GetEnumerator()????{????????foreach?(string?s?in?_names)????????{????????????yield?return?s;????????}????}}class?Test?
{
????static?void?Main()
????
{
????????Person?arrPerson?=?new?Person("oec2003","oec2004","oec2005");
????????foreach?(string?p?in?arrPerson)
????????{
????????????Console.WriteLine(p);
????????}
????????Console.ReadLine();
????}
}
public?class?Person:IEnumerable?
{
????public?Person(params?string[]?names)
????
{
????????_names?=?new?string[names.Length];
????????names.CopyTo(_names,?0);
????}
????public?string[]?_names;
????public?IEnumerator?GetEnumerator()
????
{
????????foreach?(string?s?in?_names)
????????{
????????????yield?return?s;
????????}
????}
}

匿名方法

匿名方法比較適用于定義必須通過委托調(diào)用的方法,用多線程來舉個例子,在C#1中代碼如下:

private?void?btnTest_Click(object?sender,?EventArgs?e){????Thread?thread?=?new?Thread(new?ThreadStart(DoWork));????thread.Start();}private?void?DoWork(){????for?(int?i?=?0;?i?<?100;?i++)????{????????Thread.Sleep(100);????????this.Invoke(new?Action<string>(this.ChangeLabel),i.ToString());????}}private?void?ChangeLabel(string?i){????label1.Text?=?i?+?"/100";}
????Thread?thread?=?new?Thread(new?ThreadStart(DoWork));
????thread.Start();
}
private?void?DoWork()
{
????for?(int?i?=?0;?i?<?100;?i++)
????{
????????Thread.Sleep(100);
????????this.Invoke(new?Action<string>(this.ChangeLabel),i.ToString());
????}
}
private?void?ChangeLabel(string?i)
{
????label1.Text?=?i?+?"/100";
}

使用C#2中的匿名方法,上面的例子中可以省去DoWork和ChangeLabel兩個方法,代碼如下:

private?void?btnTest_Click(object?sender,?EventArgs?e){????Thread?thread?=?new?Thread(new?ThreadStart(delegate()?{????????for?(int?i?=?0;?i?<?100;?i++)????????{????????????Thread.Sleep(100);????????????this.Invoke(new?Action(delegate()?{?label1.Text?=?i?+?"/100";?}));????????}????}));????thread.Start();}
????Thread?thread?=?new?Thread(new?ThreadStart(delegate()?{
????????for?(int?i?=?0;?i?<?100;?i++)
????????{
????????????Thread.Sleep(100);
????????????this.Invoke(new?Action(delegate()?{?label1.Text?=?i?+?"/100";?}));
????????}
????}));
????thread.Start();
}

其他相關(guān)特性

C#3.0

C#VS版本CLR版本.NET Framework
3.0VS20082.03.0 3.5

如果說C#2中的核心是泛型的話,那么C#3中的核心就應(yīng)是Linq了,C#3中的特性幾乎都是為Linq服務(wù)的,但每一項(xiàng)特性都可以脫離Linq來使用。下面就來看下C#3中有哪些特性。

自動實(shí)現(xiàn)的屬性

這個特性非常簡單,就是使定義屬性變得更簡單了。代碼如下:

public?string?Name?{?get;?set;?}public?int?Age?{?private?set;?get;?}string?Name?{?get;?set;?}
public?int?Age?{?private?set;?get;?}

隱式類型的局部變量和擴(kuò)展方法

隱式類型的局部變量是讓我們在定義變量時(shí)可以比較動態(tài)化,使用var關(guān)鍵字作為類型的占位符,然后由編譯器來推導(dǎo)變量的類型。

擴(kuò)展方法可以在現(xiàn)有的類型上添加一些自定義的方法,比如可以在string類型上添加一個擴(kuò)展方法ToInt32,就可以像“20”.ToInt32()這樣調(diào)用了。

具體參見《C#3.0學(xué)習(xí)(1)—隱含類型局部變量和擴(kuò)展方法(http://blog.fwhyy.com/2008/02/learning-csharp-3-0-1-implied-type-of-local-variables-and-extension-methods/)》。

隱式類型雖然讓編碼方便了,但有些不少限制:

對象集合初始化器

簡化了對象和集合的創(chuàng)建,具體參見《C#3.0學(xué)習(xí)(2)—對象集合初始化器(http://blog.fwhyy.com/2008/02/learning-c-3-0-2-object-collection-initializer/)》。

隱式類型的數(shù)組

和隱式類型的局部變量類似,可以不用顯示指定類型來進(jìn)行數(shù)組的定義,通常我們定義數(shù)組是這樣:

string[]?names?=?{?"oec2003",?"oec2004",?"oec2005"?};"oec2003",?"oec2004",?"oec2005"?};

使用匿名類型數(shù)組可以想下面這樣定義:

protected?void?Page_Load(object?sender,?EventArgs?e){????GetName(new[]?{?"oec2003",?"oec2004",?"oec2005"?});}public?string?GetName(string[]?names){????return?names[0];}
????GetName(new[]?{?"oec2003",?"oec2004",?"oec2005"?});
}
public?string?GetName(string[]?names)
{
????return?names[0];
}

匿名類型

匿名類型是在初始化的時(shí)候根據(jù)初始化列表自動產(chǎn)生類型的一種機(jī)制,利用對象初始化器來創(chuàng)建匿名對象的對象,具體參見《C#3.0學(xué)習(xí)(3)—匿名類型(http://blog.fwhyy.com/2008/03/learning-csharp-3-0-3-anonymous-types/)》。

Lambda表達(dá)式

實(shí)際上是一個匿名方法,Lambda表達(dá)的表現(xiàn)形式是:(參數(shù)列表)=>{語句},看一個例子,創(chuàng)建一個委托實(shí)例,獲取一個string類型的字符串,并返回字符串的長度。代碼如下:

Func<string,?int>?func?=?delegate(string?s)?{?return?s.Length;?};Console.WriteLine(func("oec2003"));int>?func?=?delegate(string?s)?{?return?s.Length;?};
Console.WriteLine(func("oec2003"));

使用Lambda的寫法如下:

Func<string,?int>?func?=?(string?s)=>?{?return?s.Length;?};Func<string,?int>?func1?=?(s)?=>?{?return?s.Length;?};Func<string,?int>?func2?=?s?=>?s.Length;int>?func?=?(string?s)=>?{?return?s.Length;?};
Func<string,?int>?func1?=?(s)?=>?{?return?s.Length;?};
Func<string,?int>?func2?=?s?=>?s.Length;

上面三種寫法是逐步簡化的過程。

Lambda表達(dá)式樹

是.NET3.5中提出的一種表達(dá)方式,提供一種抽象的方式將一些代碼表示成一個對象樹。要使用Lambda表達(dá)式樹需要引用命名空間System.Linq.Expressions,下面代碼構(gòu)建一個1+2的表達(dá)式樹,最終表達(dá)式樹編譯成委托來得到執(zhí)行結(jié)果:

Expression?a?=?Expression.Constant(1);Expression?b?=?Expression.Constant(2);Expression?add?=?Expression.Add(a,?b);Console.WriteLine(add);?//(1+2)?Func<int>?fAdd?=?Expression.Lambda<Func<int>>(add).Compile();Console.WriteLine(fAdd());?//3?
Expression?b?=?Expression.Constant(2);
Expression?add?=?Expression.Add(a,?b);
Console.WriteLine(add);?//(1+2)?Func<int>?fAdd?=?Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(fAdd());?//3?

Lambda和Lambda表達(dá)式樹為我們使用Linq提供了很多支持,如果我們在做的一個管理系統(tǒng)使用了Linq To Sql,在列表頁會有按多個條件來進(jìn)行數(shù)據(jù)的篩選的功能,這時(shí)就可以使用Lambda表達(dá)式樹來進(jìn)行封裝查詢條件,下面的類封裝了And和Or兩種條件:

public?static?class?DynamicLinqExpressions?{????public?static?Expression<Func<T,?bool>>?True<T>()?{?return?f?=>?true;?}????public?static?Expression<Func<T,?bool>>?False<T>()?{?return?f?=>?false;?}????public?static?Expression<Func<T,?bool>>?Or<T>(this?Expression<Func<T,?bool>>?expr1,????????????????????????????????????????????????????????Expression<Func<T,?bool>>?expr2)????{????????var?invokedExpr?=?Expression.Invoke(expr2,?expr1.Parameters.Cast<Expression>());????????return?Expression.Lambda<Func<T,?bool>>??????????????(Expression.Or(expr1.Body,?invokedExpr),?expr1.Parameters);????}????public?static?Expression<Func<T,?bool>>?And<T>(this?Expression<Func<T,?bool>>?expr1,?????????????????????????????????????????????????????????Expression<Func<T,?bool>>?expr2)????{????????var?invokedExpr?=?Expression.Invoke(expr2,?expr1.Parameters.Cast<Expression>());????????return?Expression.Lambda<Func<T,?bool>>??????????????(Expression.And(expr1.Body,?invokedExpr),?expr1.Parameters);????}}static?class?DynamicLinqExpressions?
{
????public?static?Expression<Func<T,?bool>>?True<T>()?{?return?f?=>?true;?}
????public?static?Expression<Func<T,?bool>>?False<T>()?{?return?f?=>?false;?}

????public?static?Expression<Func<T,?bool>>?Or<T>(this?Expression<Func<T,?bool>>?expr1,
????????????????????????????????????????????????????????Expression<Func<T,?bool>>?expr2)
????{
????????var?invokedExpr?=?Expression.Invoke(expr2,?expr1.Parameters.Cast<Expression>());
????????return?Expression.Lambda<Func<T,?bool>>
??????????????(Expression.Or(expr1.Body,?invokedExpr),?expr1.Parameters);
????}

????public?static?Expression<Func<T,?bool>>?And<T>(this?Expression<Func<T,?bool>>?expr1,
?????????????????????????????????????????????????????????Expression<Func<T,?bool>>?expr2)
????{
????????var?invokedExpr?=?Expression.Invoke(expr2,?expr1.Parameters.Cast<Expression>());
????????return?Expression.Lambda<Func<T,?bool>>
??????????????(Expression.And(expr1.Body,?invokedExpr),?expr1.Parameters);
????}
}

下面是獲取條件的方法:

public?Expression<Func<Courses,?bool>>?GetCondition(){????var?exp?=?DynamicLinqExpressions.True<Courses>();????if?(txtCourseName.Text.Trim().Length?>?0)????{????????exp?=?exp.And(g?=>?g.CourseName.Contains(txtCourseName.Text.Trim()));????}????if?(ddlGrade.SelectedValue?!=?"-1")????{????????exp=exp.And(g?=>?g.GradeID.Equals(ddlGrade.SelectedValue));????}????return?exp;}
{
????var?exp?=?DynamicLinqExpressions.True<Courses>();
????if?(txtCourseName.Text.Trim().Length?>?0)
????{
????????exp?=?exp.And(g?=>?g.CourseName.Contains(txtCourseName.Text.Trim()));
????}
????if?(ddlGrade.SelectedValue?!=?"-1")
????{
????????exp=exp.And(g?=>?g.GradeID.Equals(ddlGrade.SelectedValue));
????}
????return?exp;
}

Linq

Linq是一個很大的話題,也是NET3.5中比較核心的內(nèi)容,有很多書籍專門來介紹Linq,下面只是做一些簡單的介紹,需要注意的是Linq并非是Linq To Sql,Linq是一個大的集合,里面包含:

下面以Linq To Object為例子來看看Linq是怎么使用的:

public?class?UserInfo?{????public?string?Name?{?get;?set;?}????public?int?Age?{?get;?set;?}}public?class?Test?{????static?void?Main()????{????????List<UserInfo>?users?=?new?List<UserInfo>()????????{????????????new?UserInfo{Name="oec2003",Age=20},????????????new?UserInfo{Name="oec2004",Age=21},????????????new?UserInfo{Name="oec2005",Age=22}????????};????????IEnumerable<UserInfo>?selectedUser?=?from?user?in?users?????????????????????????????????????????????where?user.Age?>?20?????????????????????????????????????????????orderby?user.Age?descending?select?user;????????foreach?(UserInfo?user?in?selectedUser)????????{????????????Console.WriteLine("姓名:"+user.Name+",年齡:"+user.Age);????????}????????Console.ReadLine();????}}class?UserInfo?
{
????public?string?Name?{?get;?set;?}
????public?int?Age?{?get;?set;?}
}
public?class?Test?
{
????static?void?Main()
????
{
????????List<UserInfo>?users?=?new?List<UserInfo>()
????????{
????????????new?UserInfo{Name="oec2003",Age=20},
????????????new?UserInfo{Name="oec2004",Age=21},
????????????new?UserInfo{Name="oec2005",Age=22}
????????};
????????IEnumerable<UserInfo>?selectedUser?=?from?user?in?users
?????????????????????????????????????????????where?user.Age?>?20
?????????????????????????????????????????????orderby?user.Age?descending?select?user;
????????foreach?(UserInfo?user?in?selectedUser)
????????{
????????????Console.WriteLine("姓名:"+user.Name+",年齡:"+user.Age);
????????}
????????Console.ReadLine();
????}
}

可以看出,Linq可以讓我們使用類似Sql的關(guān)鍵字來對集合、對象、XML等進(jìn)行查詢。

C#4.0

C#VS版本CLR版本.NET Framework
4.0VS20104.04.0

可選參數(shù)

VB在很早就已經(jīng)支持了可選參數(shù),而C#知道4了才支持,顧名思義,可選參數(shù)就是一些參數(shù)可以是可選的,在方法調(diào)用的時(shí)候可以不用輸入。看下面代碼:

public?class?Test?{????static?void?Main()????{????????Console.WriteLine(GetUserInfo());?//姓名:ooec2003,年齡:30?????????Console.WriteLine(GetUserInfo("oec2004",?20));//姓名:ooec2004,年齡:20?????????Console.ReadLine();????}????public?static?string?GetUserInfo(string?name?=?"oec2003",?int?age?=?30)????{????????return?"姓名:"?+?name?+?",年齡:"?+?age.ToString();????}}class?Test?
{

????static?void?Main()
????
{
????????Console.WriteLine(GetUserInfo());?//姓名:ooec2003,年齡:30?
????????Console.WriteLine(GetUserInfo("oec2004",?20));//姓名:ooec2004,年齡:20?
????????Console.ReadLine();
????}
????public?static?string?GetUserInfo(string?name?=?"oec2003",?int?age?=?30)
????
{
????????return?"姓名:"?+?name?+?",年齡:"?+?age.ToString();
????}
}

命名實(shí)參

命名實(shí)參是在制定實(shí)參的值時(shí),可以同時(shí)指定相應(yīng)參數(shù)的名稱。編譯器可以判斷參數(shù)的名稱是否正確,命名實(shí)參可以讓我們在調(diào)用時(shí)改變參數(shù)的順序。命名實(shí)參也經(jīng)常和可選參數(shù)一起使用,看下面的代碼:

static?void?Main(){????Console.WriteLine(Cal());//9?????Console.WriteLine(Cal(z:?5,?y:?4));//25?????Console.ReadLine();}public?static?int?Cal(int?x=1,?int?y=2,?int?z=3){????return?(x?+?y)?*?z;}
????Console.WriteLine(Cal());//9?
????Console.WriteLine(Cal(z:?5,?y:?4));//25?
????Console.ReadLine();
}
public?static?int?Cal(int?x=1,?int?y=2,?int?z=3)
{
????return?(x?+?y)?*?z;
}

通過可選參數(shù)和命名參數(shù)的結(jié)合使用,我們可以減少代碼中方法的重載。

動態(tài)類型

C#使用dynamic來實(shí)現(xiàn)動態(tài)類型,在沒用使用dynamic的地方,C#依然是靜態(tài)的。靜態(tài)類型中當(dāng)我們要使用程序集中的類,要調(diào)用類中的方法,編譯器必須知道程序集中有這個類,類里有這個方法,如果不能事先知道,編譯時(shí)會報(bào)錯,在C#4以前可以通過反射來解決這個問題。看一個使用dynamic的小例子:

dynamic?a?=?"oec2003";Console.WriteLine(a.Length);//7?Console.WriteLine(a.length);//string?類型不包含length屬性,但編譯不會報(bào)錯,運(yùn)行時(shí)會報(bào)錯?Console.ReadLine();"oec2003";
Console.WriteLine(a.Length);//7?
Console.WriteLine(a.length);//string?類型不包含length屬性,但編譯不會報(bào)錯,運(yùn)行時(shí)會報(bào)錯?
Console.ReadLine();

您可能會發(fā)現(xiàn)使用dynamic聲明變量和C#3中提供的var有點(diǎn)類似,其他他們是有本質(zhì)區(qū)別的,var聲明的變量在編譯時(shí)會去推斷出實(shí)際的類型,var只是相當(dāng)于一個占位符,而dynamic聲明的變量在編譯時(shí)不會進(jìn)行類型檢查。

dynamic用的比較多的應(yīng)該是替代以前的反射,而且性能有很大提高。假設(shè)有一個名為DynamicLib的程序集中有一個DynamicClassDemo類,類中有一個Cal方法,下面看看利用反射怎么訪問Cal方法:

namespace?DynamicLib{????public?class?DynamicClassDemo?????{????????public?int?Cal(int?x?=?1,?int?y?=?2,?int?z?=?3)????????{????????????return?(x?+?y)?*?z;????????}????}}static?void?Main(){????Assembly?assembly?=?Assembly.Load("DynamicLib");????object?obj?=?assembly.CreateInstance("DynamicLib.DynamicClassDemo");????Type?type?=?obj.GetType();????MethodInfo?method?=?type.GetMethod("Cal");????Console.WriteLine(method.Invoke(obj,?new?object[]?{?1,?2,?3?}));//9?????Console.ReadLine();}DynamicLib
{
????public?class?DynamicClassDemo?
????{
????????public?int?Cal(int?x?=?1,?int?y?=?2,?int?z?=?3)
????????
{
????????????return?(x?+?y)?*?z;
????????}
????}
}
static?void?Main()
{
????Assembly?assembly?=?Assembly.Load("DynamicLib");
????object?obj?=?assembly.CreateInstance("DynamicLib.DynamicClassDemo");
????Type?type?=?obj.GetType();
????MethodInfo?method?=?type.GetMethod("Cal");
????Console.WriteLine(method.Invoke(obj,?new?object[]?{?1,?2,?3?}));//9?
????Console.ReadLine();
}

用dynamic的代碼如下:

Assembly?assembly?=?Assembly.Load("DynamicLib");dynamic?obj?=?assembly.CreateInstance("DynamicLib.DynamicClassDemo");Console.WriteLine(obj.Cal());Console.ReadLine();assembly?=?Assembly.Load("DynamicLib");
dynamic?obj?=?assembly.CreateInstance("DynamicLib.DynamicClassDemo");
Console.WriteLine(obj.Cal());
Console.ReadLine();

在前后端分離的模式下,WebAPI接口的參數(shù)也可以采用dynamic來定義,直接就可以解析前端傳入的json參數(shù),不用每一個接口方法都定義一個參數(shù)類型。不好的地方就是通過Swagger來生產(chǎn)API文檔時(shí),不能明確的知道輸入?yún)?shù)的每個屬性的含義。

C#4中還有一些COM互操作性的改進(jìn)和逆變性和協(xié)變性的改進(jìn),我?guī)缀鯖]有用到,所以在此就不講述了。

C#5.0

C#VS版本CLR版本.NET Framework
5.0VS2012\20134.04.5

異步處理

異步處理是C#5中很重要的一個特性,會涉及到兩個關(guān)鍵字:async和await,要講明白這個需要單獨(dú)寫一篇來介紹。

可以簡單理解為,當(dāng)Winform窗體程序中有一個耗時(shí)操作時(shí),如果是同步操作,窗體在返回結(jié)果之前會卡死,當(dāng)然在C#5之前的版本中有多種方法可以來解決這個問題,但C#5的異步處理解決的更優(yōu)雅。

循環(huán)中捕獲變量

與其說是一個特性,不如說是對之前版本問題的修復(fù),看下面的代碼:

public?static?void?CapturingVariables(){????string[]?names?=?{?"oec2003","oec2004","oec2005"};????var?actions?=?new?List<Action>();????foreach(var?name?in?names)????{????????actions.Add(()?=>?Console.WriteLine(name));????}????foreach(Action?action?in?actions)????{????????action();????}}
????string[]?names?=?{?"oec2003","oec2004","oec2005"};
????var?actions?=?new?List<Action>();

????foreach(var?name?in?names)
????{
????????actions.Add(()?=>?Console.WriteLine(name));
????}
????foreach(Action?action?in?actions)
????{
????????action();
????}
}

這段代碼在之前的C#版本中,會連續(xù)輸出三個oec2005,在C#5中會按照我們的期望依次輸出oec2003、oec2004、oec2005。

如果您的代碼在之前的版本中有利用到這個錯誤的結(jié)果,那么在升級到C#5或以上版本中就要注意了。

調(diào)用者信息特性

我們的程序通常是以release形式發(fā)布,發(fā)布后很難追蹤到代碼執(zhí)行的具體信息,在C#5中提供了三種特性(Attribute), 允許獲取調(diào)用者的當(dāng)前編譯器的執(zhí)行文件名、所在行數(shù)與方法或?qū)傩悦Q。代碼如下:

static?void?Main(string[]?args){????ShowInfo();????Console.ReadLine();}public?static?void?ShowInfo(???[CallerFilePath]?string?file?=?null,???[CallerLineNumber]?int?number?=?0,???[CallerMemberName]?string?name?=?null){????Console.WriteLine($"filepath:{file}");????Console.WriteLine($"rownumber:{number}");????Console.WriteLine($"methodname:{name}");}
????ShowInfo();

????Console.ReadLine();

}
public?static?void?ShowInfo(
???[CallerFilePath]?string?file?=?null,
???[CallerLineNumber]?int?number?=?0,
???[CallerMemberName]?string?name?=?null
)
{
????Console.WriteLine($"filepath:{file}");
????Console.WriteLine($"rownumber:{number}");
????Console.WriteLine($"methodname:{name}");
}

調(diào)用結(jié)果如下:

filepath:/Users/ican_macbookpro/Projects/CsharpFeature/CsharpFeature5/Program.csrownumber:12methodname:Main
rownumber:12
methodname:Main

C#6.0

C#VS版本CLR版本.NET Framework
6.0VS20154.04.6

在C#6中提供了不少的新功能,我認(rèn)為最有用的就是Null條件運(yùn)算符和字符串嵌入。

Null條件運(yùn)算符

在C#中,一個常見的異常就是“未將對象引用到對象的實(shí)例”,原因是對引用對象沒有做非空判斷導(dǎo)致。在團(tuán)隊(duì)中雖然再三強(qiáng)調(diào),但依然會在這個問題上栽跟頭。下面的代碼就會導(dǎo)致這個錯誤:

class?Program{????static?void?Main(string[]?args)????{????????//Null條件運(yùn)算符????????User?user?=?null;????????Console.WriteLine(user.GetUserName());????????Console.ReadLine();????}}class?User{????public?string?GetUserName()?=>?"oec2003";}Program
{
????static?void?Main(string[]?args)
????
{
????????//Null條件運(yùn)算符
????????User?user?=?null;
????????Console.WriteLine(user.GetUserName());
????????Console.ReadLine();
????}
}
class?User
{
????public?string?GetUserName()?=>?"oec2003";
}

要想不出錯,就需要對user對象做非空判斷

if(user!=null){????Console.WriteLine(user.GetUserName());?}null)
{
????Console.WriteLine(user.GetUserName());?
}

在C#6中可以用很簡單的方式來處理這個問題

//Null條件運(yùn)算符User?user?=?null;Console.WriteLine(user?.GetUserName());?
User?user?=?null;
Console.WriteLine(user?.GetUserName());?

注:雖然這個語法糖非常簡單,也很好用,但在使用時(shí)也需要多想一步,當(dāng)對象為空時(shí),調(diào)用其方法返回的值也是空,這樣的值對后續(xù)的操作會不會有影響,如果有,還是需要做判斷,并做相關(guān)的處理。

字符串嵌入

字符串嵌入可以簡化字符串的拼接,很直觀的就可以知道需要表達(dá)的意思,在C#6及以上版本中都應(yīng)該用這種方式來處理字符串拼接,代碼如下:

//字符串嵌入string?name?=?"oec2003";//之前版本的處理方式1Console.WriteLine("Hello?"?+?name);//之前版本的處理方式2Console.WriteLine(string.Format("Hello?{0}",name));//C#6字符串嵌入的處理方式Console.WriteLine($"Hello?{name}");
string?name?=?"oec2003";
//之前版本的處理方式1
Console.WriteLine("Hello?"?+?name);
//之前版本的處理方式2
Console.WriteLine(string.Format("Hello?{0}",name));
//C#6字符串嵌入的處理方式
Console.WriteLine($"Hello?{name}");

其他相關(guān)特性

C#7.0

C#VS版本.NET Framework
7.0VS2017 15.0.NET Core1.0
7.1VS2017 15.3.NET Core2.0
7.2VS2017 15.5.NET Core2.0
7.3VS2017 15.7.NET Core2.1

out 變量

此特性簡化了out變量的使用,之前的版本中使用代碼如下:

int?result?=?0;int.TryParse("20",?out?result);Console.WriteLine(result);0;
int.TryParse("20",?out?result);
Console.WriteLine(result);

優(yōu)化后的代碼,不需要事先定義一個變量

int.TryParse("20",?out?var?result);Console.WriteLine(result);"20",?out?var?result);
Console.WriteLine(result);

模式匹配

這也是一個減少我們編碼的語法糖,直接看代碼吧

public?class?PatternMatching{????public?void?Test()????{????????List<Person>?list?=?new?List<Person>();????????list.Add(new?Man());????????list.Add(new?Woman());????????foreach?(var?item?in?list)????????{?????????????????//在之前版本中此處需要做類型判斷和類型轉(zhuǎn)換????????????if?(item?is?Man?man)????????????????Console.WriteLine(man.GetName());????????????else?if?(item?is?Woman?woman)????????????????Console.WriteLine(woman.GetName());????????}????}}public?abstract?class?Person{????public?abstract?string?GetName();}public?class?Man:Person{????public?override?string?GetName()?=>?"Man";}public?class?Woman?:?Person{????public?override?string?GetName()?=>?"Woman";}class?PatternMatching
{
????public?void?Test()
????
{
????????List<Person>?list?=?new?List<Person>();
????????list.Add(new?Man());
????????list.Add(new?Woman());
????????foreach?(var?item?in?list)
????????{
?????????????????//在之前版本中此處需要做類型判斷和類型轉(zhuǎn)換
????????????if?(item?is?Man?man)
????????????????Console.WriteLine(man.GetName());
????????????else?if?(item?is?Woman?woman)
????????????????Console.WriteLine(woman.GetName());
????????}
????}
}
public?abstract?class?Person
{
????public?abstract?string?GetName();
}
public?class?Man:Person
{
????public?override?string?GetName()?=>?"Man";
}
public?class?Woman?:?Person
{
????public?override?string?GetName()?=>?"Woman";
}

詳細(xì)參考官方文檔:https://docs.microsoft.com/zh-cn/dotnet/csharp/pattern-matching

本地方法

可以在方法中寫內(nèi)部方法,在方法中有時(shí)需要在多個代碼邏輯執(zhí)行相同的處理,之前的做法是在類中寫私有方法,現(xiàn)在可以讓這個私有方法寫在方法的內(nèi)部,提高代碼可讀性。

static?void?LocalMethod(){????string?name?=?"oec2003";????string?name1?=?"oec2004";????Console.WriteLine(AddPrefix(name));????Console.WriteLine(AddPrefix(name1));????string?AddPrefix(string?n)????{????????return?$"Hello?{n}";????}}
????string?name?=?"oec2003";
????string?name1?=?"oec2004";

????Console.WriteLine(AddPrefix(name));
????Console.WriteLine(AddPrefix(name1));

????string?AddPrefix(string?n)
????
{
????????return?$"Hello?{n}";
????}
}

異步 main 方法

這個最大的好處是,在控制臺程序中調(diào)試異步方法變得很方便。

static?async?Task?Main(){????await?SomeAsyncMethod();}
????await?SomeAsyncMethod();
}

private protected 訪問修飾符

可以限制在同一個程序集中的派生類的訪問,是對protected internal的一種補(bǔ)強(qiáng),protected internal是指同一程序集中的類或派生類進(jìn)行訪問。

其他相關(guān)特性

總結(jié)

每個特性都需要我們?nèi)ゾ幋a實(shí)現(xiàn)下,了解了真正的含義和用途,我們才能在工作中靈活的運(yùn)用。

本文所涉及到的實(shí)例代碼后面也會上傳到Github上。

希望本文對您有所幫助。

總結(jié)

以上是生活随笔為你收集整理的带你了解C#每个版本新特性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。