C# 語(yǔ)言歷史版本特性(C# 1.0到C# 8.0匯總)
C# 1.0 特性
Classes:面向?qū)ο筇匦裕С诸愵愋?/p>
Structs:結(jié)構(gòu)
Interfaces:接口
Events:事件
Properties:屬性仆百,類的成員奔脐,提供訪問(wèn)字段的靈活方法
Delegates:委托,一種引用類型,表示對(duì)具有特定參數(shù)列表和返回類型的方法的引用
Expressions,Statements,Operators:表達(dá)式峦朗、語(yǔ)句排龄、操作符
Attributes:特性橄维,為程序代碼添加元數(shù)據(jù)或聲明性信息争舞,運(yùn)行時(shí),通過(guò)反射可以訪問(wèn)特性信息
Literals:字面值(或理解為常量值)侄非,區(qū)別常量,常量是和變量相對(duì)的
C# 2.0 特性 (VS 2005)
Generics:泛型
Partial types:分部類型者疤,可以將類驹马、結(jié)構(gòu)除秀、接口等類型定義拆分到多個(gè)文件中
Anonymous methods:匿名方法
Iterators:迭代器
Nullable types:可以為Null的類型册踩,該類可以是其它值或者null
Getter/setter separate accessibility:屬性訪問(wèn)控制
Method group conversions (delegates):方法組轉(zhuǎn)換暂吉,可以將聲明委托代表一組方法,隱式調(diào)用
Co- and Contra-variance for delegates and interfaces:委托阎肝、接口的協(xié)變和逆變
Static classes:靜態(tài)類
Delegate inference:委托推斷风题,允許將方法名直接賦給委托變量
C# 3.0 特性(VS 2008)
Implicitly typed local variables:
Object and collection initializers:對(duì)象和集合初始化器
Auto-Implemented properties:自動(dòng)屬性嫉父,自動(dòng)生成屬性方法熔号,聲明更簡(jiǎn)潔
Anonymous types:匿名類型
Extension methods:擴(kuò)展方法
Query expressions:查詢表達(dá)式
Lambda expression:Lambda表達(dá)式
Expression trees:表達(dá)式樹引镊,以樹形數(shù)據(jù)結(jié)構(gòu)表示代碼篮条,是一種新數(shù)據(jù)類型
Partial methods:部分方法
C# 4.0特性 (VS 2010)
Dynamic binding:動(dòng)態(tài)綁定
Named and optional arguments:命名參數(shù)和可選參數(shù)
Generic co- and contravariance:泛型的協(xié)變和逆變
Embedded interop types (“NoPIA”):開(kāi)啟嵌入類型信息涉茧,增加引用COM組件程序的中立性
C# 5.0特性 (VS 2012)
Asynchronous methods:異步方法
Caller info attributes:調(diào)用方信息特性伴栓,調(diào)用時(shí)訪問(wèn)調(diào)用者的信息
C# 6特征 (VS 2015)
Compiler-as-a-service (Roslyn)
Import of static type members into namespace:支持僅導(dǎo)入類中的靜態(tài)成員
Exception filters:異常過(guò)濾器
Await in catch/finally blocks:支持在catch/finally語(yǔ)句塊使用await語(yǔ)句
Auto property initializers:自動(dòng)屬性初始化
Default values for getter-only properties:設(shè)置只讀屬性的默認(rèn)值
Expression-bodied members:支持以表達(dá)式為主體的成員方法和只讀屬性
Null propagator (null-conditional operator, succinct null checking):Null條件操作符
String interpolation:字符串插值,產(chǎn)生特定格式字符串的新方法
nameof operator:nameof操作符额港,返回方法移斩、屬性绢馍、變量的名稱
Dictionary initializer:字典初始化
C# 7.0 特征 (Visual Studio 2017)
Out variables:out變量直接聲明舰涌,例如可以out in parameter
Pattern matching:模式匹配瓷耙,根據(jù)對(duì)象類型或者其它屬性實(shí)現(xiàn)方法派發(fā)
Tuples:元組
Deconstruction:元組解析
Discards:沒(méi)有命名的變量哺徊,只是占位,后面代碼不需要使用其值
Local Functions:局部函數(shù)
Binary Literals:二進(jìn)制字面量
Digit Separators:數(shù)字分隔符
Ref returns and locals:引用返回值和局部變量
Generalized async return types:async中使用泛型返回類型
More expression-bodied members:允許構(gòu)造器盈滴、解析器巢钓、屬性可以使用表達(dá)式作為body
Throw expressions:Throw可以在表達(dá)式中使用
C# 7.1 特征 (Visual Studio 2017 version 15.3)
Async main:在main方法用async方式
Default expressions:引入新的字面值default
Reference assemblies:
Inferred tuple element names:
Pattern-matching with generics:
**C# 8.0 特征 (Visual Studio 2017 version 15.7)
Default Interface Methods 缺省接口實(shí)現(xiàn)
Nullable reference type NullableReferenceTypes 非空和可控的數(shù)據(jù)類型
Recursive patterns 遞歸模式
Async streams 異步數(shù)據(jù)流
Caller expression attribute 調(diào)用方法表達(dá)式屬性
Target-typed new
Generic attributes 通用屬性
Ranges
Default in deconstruction
Relax ordering of ref and partial modifiers
C#5.0版本
像C#4.0版本一樣症汹,C#5.0版本中沒(méi)有太多功能 - 但是其中一個(gè)功能非常龐大背镇。
異步/等待
CallerInfoAttributes
當(dāng)C#5.0發(fā)布時(shí)瞒斩,它實(shí)際上改變了C#開(kāi)發(fā)人員編寫異步代碼的方式胸囱。雖然直到今天仍然有很多困惑烹笔,但我在這里向您保證谤职,這比大多數(shù)人想象的要簡(jiǎn)單得多柬帕。這是C#的一個(gè)重大飛躍 - 它引入了一個(gè)語(yǔ)言級(jí)別的異步模型陷寝,它極大地賦予了開(kāi)發(fā)人員編寫外觀和感覺(jué)同步(或者至少是連續(xù)的)的“異步”代碼。
異步編程在處理I/O綁定工作負(fù)載(如與數(shù)據(jù)庫(kù)爆安,網(wǎng)絡(luò)扔仓,文件系統(tǒng)等進(jìn)行交互)時(shí)非常強(qiáng)大咖耘。異步編程通過(guò)使用非阻塞方法幫助處理吞吐量儿倒。這種方法使用了一個(gè)透明的異步狀態(tài)機(jī)中的掛點(diǎn)和相應(yīng)的延續(xù)夫否。
同樣,如果CPU負(fù)載計(jì)算的工作量很大汞幢,則可能需要考慮異步執(zhí)行此項(xiàng)工作森篷。這將有助于用戶體驗(yàn)堰酿,因?yàn)閁I線程不會(huì)被阻塞触创,而是可以自由地響應(yīng)其他UI交互哼绑。
編者注:這里有一些關(guān)于C#異步編程的最佳實(shí)踐,使用Async Await.
在C#5.0中蛀恩,當(dāng)語(yǔ)言添加了兩個(gè)新的關(guān)鍵字async和await時(shí)双谆,異步編程被簡(jiǎn)化了顽馋。這些關(guān)鍵字適用于Task寸谜。下表將作為參考:
Task表示異步操作属桦。操作可以通過(guò)Task返回值聂宾,也可以通過(guò)Task返回void系谐。當(dāng)您使用async關(guān)鍵字修飾Task返回方法時(shí)蔚鸥,它使方法主體可以使用await關(guān)鍵字。當(dāng)您請(qǐng)求await關(guān)鍵字的返回值時(shí),控制流將返回給調(diào)用者乾巧,并且在方法的那個(gè)點(diǎn)執(zhí)行暫停沟于。當(dāng)await的操作完成后,在同一點(diǎn)上恢復(fù)執(zhí)行供璧。部分代碼如下!
class IOBoundAsyncExample
{
?
? ? private const string Url = "http://api.icndb.com/jokes/random?limitTo=[nerdy]";
? ? internal async Task GetJokeAsync()
? ? {
? ? ? ? using (var client = new HttpClient())
? ? ? ? {
? ? ? ? ? ? var response = await client.GetStringAsync(Url);
? ? ? ? ? ? var result = JsonConvert.DeserializeObject(response);
? ? ? ? ? ? return result.Value.Joke;
? ? ? ? }
? ? }
}
public class Result
{
? ? [JsonProperty("type")] public string Type { get; set; }
? ? [JsonProperty("value")] public Value Value { get; set; }
}
public class Value
{
? ? [JsonProperty("id")] public int Id { get; set; }
? ? [JsonProperty("joke")] public string Joke { get; set; }
}
我們用一個(gè)名為GetJokeAsync的方法定義一個(gè)簡(jiǎn)單的類来惧。該方法是返回Task供搀,這意味著我們的GetJokeAsync方法最終會(huì)給您一個(gè)字符串趁曼,或者可能出錯(cuò)挡闰。
該方法使用async關(guān)鍵字進(jìn)行修飾,該關(guān)鍵字允許使用等待關(guān)鍵字奢驯。我們實(shí)例化并使用一個(gè)HttpClient對(duì)象瘪阁。然后我們調(diào)用GetStringAsync函數(shù),它接受一個(gè)字符串url并返回一個(gè)Task 豁跑。我們等待從GetStringAsync調(diào)用返回的Task。
當(dāng)響應(yīng)已經(jīng)準(zhǔn)備好時(shí)卸夕,就會(huì)繼續(xù)發(fā)生并控制從我們?cè)?jīng)掛起的位置恢復(fù)。然后碍讨,我們將JSON反序列化到Result類的實(shí)例中,并返回Joke屬性覆获。
一些我最喜歡的成果
查克·諾里斯(Chuck Norris)可以用單一的斷言來(lái)測(cè)試整個(gè)應(yīng)用程序弄息。
查克·諾里斯(Chuck Norris)可以編譯語(yǔ)法錯(cuò)誤。
項(xiàng)目經(jīng)理永遠(yuǎn)不會(huì)要求查克·諾里斯(Chuck Norris)做出估計(jì)。
歡鬧隨之而來(lái)睦尽!我們了解了C#5.0的驚人的異步編程模型。
C#6.0版本
C#6.0的推出有很多很大的進(jìn)步型雳,很難選擇我最喜歡的功能当凡。
字典初始化
異常過(guò)濾器
在屬性里使用Lambda表達(dá)式
nameof表達(dá)式
空值運(yùn)算符
自動(dòng)屬性初始化
靜態(tài)導(dǎo)入
字符串嵌入值
我把范圍縮小到三個(gè)突出特點(diǎn):空值運(yùn)算符,字符串嵌入值和nameof表達(dá)式纠俭。
雖然nameof表達(dá)式很棒沿量,我?guī)缀趺看味加盟鼇?lái)編寫代碼,但其他兩個(gè)特性更有影響力冤荆。這讓我在字符串嵌入值和空值運(yùn)算符之間做出決定欧瘪,這是相當(dāng)困難的。我決定我最喜歡的是字符串嵌入值匙赞,這就是為什么佛掖。
空值運(yùn)算符是偉大的,它允許我寫較少的詳細(xì)代碼涌庭,但它不一定能防止我的代碼中的錯(cuò)誤芥被。但是,使用字符串嵌入值可以防止運(yùn)行時(shí)錯(cuò)誤 - 這是我的書中的一個(gè)勝利坐榆。
使用$符號(hào)啟動(dòng)字符串文字時(shí)拴魄,將啟用C#中的字符串嵌入值語(yǔ)法。這指示C#編譯器打算用各種C#變量,邏輯或表達(dá)式來(lái)插入此字符串匹中。這是手動(dòng)字符串連接甚至是string.Format方法的一個(gè)主要升級(jí)夏漱。考慮以下:
{
? ? public string FirstName { get; set; }
? ? public string LastName { get; set; }
? ? public override string ToString()
? ? ? ? => string.Format("{0} {1}", FirstName);
}
我們有一個(gè)簡(jiǎn)單的Person類顶捷,具有兩個(gè)名稱屬性挂绰,用于名字和姓氏。我們重寫ToString方法并使用string.Format服赎。問(wèn)題是葵蒂,編譯時(shí),由于開(kāi)發(fā)人員顯然希望將姓氏也作為結(jié)果字符串的一部分重虑,因此很容易出錯(cuò)践付,這一點(diǎn)在“{0} {1} ”參數(shù)中很明顯。同樣缺厉,開(kāi)發(fā)人員可以很容易地交換名稱或正確提供兩個(gè)名稱參數(shù)永高,但混亂的格式文字只包括第一個(gè)索引,等等...現(xiàn)在我們可以考慮使用字符串嵌入值提针。
class Person
{
? ? public string FirstName { get; set; } = "David";
? ? public string LastName { get; set; } = "Pine";
? ? public DateTime DateOfBirth { get; set; } = new DateTime(1984, 7, 7);
? ? public override string ToString()
? ? ? ? => $"{FirstName} {LastName} (Born {DateOfBirth:MMMM dd, yyyy})";
}
我冒昧添加DateOfBirth屬性和一些默認(rèn)的屬性值命爬。另外,我們現(xiàn)在在我們的ToString方法的覆蓋中使用字符串嵌入值关贵。作為一名開(kāi)發(fā)人員遇骑,犯上述錯(cuò)誤要困難得多。最后揖曾,我也可以在插值表達(dá)式中進(jìn)行格式化落萎。注意第三次嵌入值,DateOfBirth是一個(gè)DateTime - 因此我們可以使用您已經(jīng)習(xí)慣的所有標(biāo)準(zhǔn)格式炭剪。只需使用:運(yùn)算符來(lái)分隔變量和格式练链。
示例輸出
· David Pine (Born July 7, 1984)
編輯注:有關(guān)C#6.0新特性的詳細(xì)內(nèi)容,請(qǐng)閱讀www.dotnetcurry.com/csharp/1042/csharp-6-new-features
C#7.0版本
從所有集成到 C# 7.0的特性中奴拦。
更多的函數(shù)成員的表達(dá)式體
局部函數(shù)
Out變量
模式匹配
局部變量和引用返回
元組和解構(gòu)
我結(jié)束了模式匹配媒鼓,元組和Out變量之間的爭(zhēng)論。我最終選擇了Out變量错妖,這是為什么绿鸣。
模式匹配但是我真的不經(jīng)常使用它,至少現(xiàn)在還沒(méi)有暂氯。也許以后我會(huì)更多地使用它潮模,但是對(duì)于我迄今為止編寫的所有c#代碼,沒(méi)有太多地方可以利用它痴施。同樣擎厢,這是一個(gè)很棒的功能究流,我確實(shí)看到了它的位置 - 只是在C#7.0中這不是我最喜歡的。
元組也是一個(gè)很好的補(bǔ)充动遭。元組是語(yǔ)言的重要組成部分芬探,成為一流的公民是非常棒的。我會(huì)說(shuō)厘惦,“寫tem1偷仿,.Item2,.Item3等...的日子已經(jīng)過(guò)去了绵估,但這并不一定是正確的炎疆。反序列化失去了元組的名稱卡骂,使得這個(gè)公共API不那么有價(jià)值
我也不喜歡ValueTuple類型是可變的這一事實(shí)国裳。我只是不明白設(shè)計(jì)者的決定。我希望有人能給我解釋一下全跨,但感覺(jué)有點(diǎn)像疏忽缝左。因此,我得到了選擇out變量的特性浓若。
自從C#版本1.0以來(lái)渺杉,try-parse模式已經(jīng)在各種值類型中出現(xiàn)了。模式如下:
public boolean TryParse(string value, out DateTime date)
{
? ? // 為了簡(jiǎn)便起見(jiàn),我們省略了.....
}
該函數(shù)返回一個(gè)布爾值挪钓,指示給定的字符串值是否能夠被解析是越。如果為true,則將分析的值分配給生成的輸出參數(shù)date碌上。它的使用如下:
DateTime date;
if (DateTime.TryParse(someDateString, out date))
{
? ? //? date現(xiàn)在是解析值
}
else
{
? ? // date是DateTime.MinValue,默認(rèn)值
}
這種模式是有用的倚评,但有點(diǎn)麻煩。有時(shí)馏予,不管解析是否成功天梧,開(kāi)發(fā)人員都會(huì)采取相同的操作過(guò)程。有時(shí)使用默認(rèn)值是可以的霞丧。C#7.0中的out變量使得這個(gè)更復(fù)雜呢岗,不過(guò)在我看來(lái)不那么復(fù)雜。
示例如下:
if (DateTime.TryParse(someDateString, out var date))
{
? ? // date現(xiàn)在是解析值
}
else
{
? ? // date是DateTime.MinValue,默認(rèn)值
}
現(xiàn)在我們移除了if語(yǔ)句塊的外部聲明蛹尝,并把聲明作為參數(shù)本身的一部分后豫。使用var是合法的,因?yàn)轭愋褪且阎耐荒恰W詈蟠炷穑琩ate變量的范圍沒(méi)有改變。它從內(nèi)聯(lián)聲明泄漏到if塊的頂部陨收。
你可能會(huì)問(wèn)自己:“為什么這是他最喜歡的功能之一?”.....這種感覺(jué)真的沒(méi)有什么變化饭豹。
但是這改變了一切鸵赖!
它使我們的C#更具有表現(xiàn)力。每個(gè)人都喜歡擴(kuò)展方法拄衰,對(duì) - 請(qǐng)考慮以下幾點(diǎn):
public static class StringExtensions
{
? ? private delegate bool TryParseDelegate(string s, out T result);
? ? private static T To(string value, TryParseDelegate parse)
? ? ? ? => parse(value, out T result) ? result : default;
? ? public static int ToInt32(this string value)
? ? ? ? => To(value, int.TryParse);
? ? public static DateTime ToDateTime(this string value)
? ? ? ? => To(value, DateTime.TryParse);
? ? public static IPAddress ToIPAddress(this string value)
? ? ? ? => To(value, IPAddress.TryParse);
? ? public static TimeSpan ToTimeSpan(this string value)
? ? ? ? => To(value, TimeSpan.TryParse);
}
這個(gè)擴(kuò)展方法類很簡(jiǎn)潔它褪,表達(dá)能力強(qiáng)。在定義了遵循try-parse模式的私有委托之后翘悉,我們可以編寫一個(gè)泛型復(fù)合函數(shù)茫打,它需要一個(gè)泛型類型的參數(shù)、要解析的字符串值和TryParseDelegate⊙欤現(xiàn)在我們可以安全地依賴這些擴(kuò)展方法老赤,考慮以下幾點(diǎn)::
public class Program
{
? ? public static void Main(string[] args)
? ? {
? ? ? ? var str =
? ? ? ? ? ? string.Join(
? ? ? ? ? ? ? ? "",
? ? ? ? ? ? ? ? new[] { "James", "Bond", " +7 " }.Select(s => s.ToInt32()));
? ? ? ? Console.WriteLine(str); // 打印 "007"
? ? }
}
要了解C#7的所有新功能,請(qǐng)查看本教程www.dotnetcurry.com/csharp/1286/csharp-7-new-expected-features
結(jié)論
這篇文章對(duì)我個(gè)人而言頗具挑戰(zhàn)性制市。我喜歡C#的許多特性抬旺,因此每次發(fā)布只收集一個(gè)最喜歡的內(nèi)容是非常困難的。
每個(gè)較新版本的C#都包含了強(qiáng)大而有影響力的功能祥楣。C#語(yǔ)言團(tuán)隊(duì)以無(wú)數(shù)的方式進(jìn)行創(chuàng)新 - 其中之一就是引入點(diǎn)發(fā)布开财。在撰寫本文時(shí),C# 7.1和 7.2已正式發(fā)貨误褪。作為C#開(kāi)發(fā)人員责鳍,我們生活在一個(gè)激動(dòng)人心的語(yǔ)言時(shí)代!
然而,對(duì)我來(lái)說(shuō)兽间,對(duì)所有這些特性進(jìn)行分類是相當(dāng)有見(jiàn)地的;因?yàn)樗鼛椭覀兞私饬耸裁词菍?shí)際的历葛,最影響我的日常發(fā)展。一如既往嘀略,努力成為一個(gè)務(wù)實(shí)的開(kāi)發(fā)者!并不是語(yǔ)言中所有可用的特性都是當(dāng)前任務(wù)所必需的恤溶,但了解什么是可用的,這一點(diǎn)很重要屎鳍。
當(dāng)我們期待C#8的建議和原型時(shí)宏娄,我對(duì)C#的未來(lái)感到興奮。它看起來(lái)確實(shí)很有希望逮壁,而且語(yǔ)言正在積極地試圖緩解“價(jià)值億萬(wàn)美金的錯(cuò)誤”孵坚。