C#7.0中新特性匯總
以下將是 C# 7.0 中所有計(jì)劃的語(yǔ)言特性的描述。隨著 Visual Studio “15” Preview 4 版本的發(fā)布,這些特性中的大部分將活躍起來(lái)?,F(xiàn)在是時(shí)候來(lái)展示這些特性,你也告訴借此告訴我們你的想法!
C#7.0 增加了許多新功能,并專(zhuān)注于數(shù)據(jù)消費(fèi),簡(jiǎn)化代碼和性能的改善?;蛟S最大的特性就是元祖和模式匹配,元祖可以很容易地?fù)碛卸鄠€(gè)返回結(jié)果,而模型匹配可以根據(jù)數(shù)據(jù)的“形”的不同來(lái)簡(jiǎn)化代碼。我們希望,將它們結(jié)合起來(lái),從而使你的代碼更加簡(jiǎn)潔高效,也可以使你更加快樂(lè)并富有成效。
請(qǐng)點(diǎn)擊 Visual Studio 窗口頂部的反饋按鈕,告訴我們哪些是你不期待的特性或者你關(guān)于提升這些特性的思考。還有許多功能沒(méi)有在 Preview 4 版本中實(shí)現(xiàn)。接下來(lái)我會(huì)描述一些我們發(fā)布的最終版本里將會(huì)起作用的特性,和一些一旦不起作用機(jī)即會(huì)刪除掉的特性。我也是支持對(duì)這些計(jì)劃作出改變,尤其是作為我們從你那兒得到反饋的結(jié)果。當(dāng)最終版本發(fā)布時(shí),這些特性中的一些將會(huì)改變或者刪除。
如果你好奇這些特性的設(shè)計(jì)過(guò)程,你可以在 Roslyn GitHub site 上找到很多設(shè)計(jì)筆記和討論。
希望 C#7.0 能帶給你快樂(lè)!
輸出變量
在當(dāng)前的 C# 中,使用輸出參數(shù)并不像我們想的那樣方便。在你調(diào)用一個(gè)無(wú)輸出參數(shù)的方法之前,首先必須聲明一個(gè)變量并傳遞給它。如果你沒(méi)有初始化這些變量,你就無(wú)法使用 var 來(lái)聲明它們,除非先指定完整的類(lèi)型:
public void PrintCoordinates(Point p) { int x, y; // have to "predeclare" p.GetCoordinates(out x, out y); WriteLine($"({x}, {y})"); }
在 C#7.0 中,我們正在增加輸出變量和聲明一個(gè)作為能夠被傳遞的輸出實(shí)參的變量的能力:
public void PrintCoordinates(Point p) { p.GetCoordinates(out int x, out int y); WriteLine($"({x}, {y})"); }
注意,變量是在封閉塊的范圍內(nèi),所以后續(xù)也可以使用它們。大多數(shù)類(lèi)型的聲明不建立自己的范圍,因此在他們中聲明的變量通常會(huì)被引入到封閉范圍。
Note:在 Preview 4 中,適用范圍規(guī)則更為嚴(yán)格:輸出變量的作用域是聲明它們的語(yǔ)句,因此直到下個(gè)版本發(fā)布時(shí),上面的示例才會(huì)起作用。
由于輸出變量直接被聲明為實(shí)參傳遞給輸出形參,編譯器通常會(huì)告訴他們應(yīng)該是的類(lèi)型(除非有沖突過(guò)載),所以使用 var 來(lái)代替聲明它們的方式是比較好的:
p.GetCoordinates(out var x, out var y);
輸出參數(shù)的一種常見(jiàn)用法是Try模式,其中一個(gè)布爾返回值表示成功,輸出參數(shù)就會(huì)攜帶所獲的結(jié)果:
public void PrintStars(string s) { if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); } else { WriteLine("Cloudy - no stars tonight!"); } }
注意:這里i只用在 if 語(yǔ)句來(lái)定義它,所以 Preview 4 可以將這個(gè)處理的很好。
我們計(jì)劃允許以 a* 為形式的“通配符”作為輸出參數(shù),這會(huì)讓你忽略了你不關(guān)心參數(shù):
p.GetCoordinates(out int x, out *); // I only care about x
Note:在 C#7.0 中是否會(huì)包含通配符還不確定。
模式匹配
C# 7.0 引入了模式概念。抽象地講,模式是句法元素,能用來(lái)測(cè)試一個(gè)數(shù)據(jù)是否具有某種“形”,并在被應(yīng)用時(shí),從值中提取有效信息。
C#7.0 中的模式示例:
•C 形式的常量模式(C是C#中的常量表達(dá)式),可以測(cè)試輸入是否等于C
•T X 形式的類(lèi)型模式(T是一種類(lèi)型、X是一個(gè)標(biāo)識(shí)符),可以測(cè)試輸入是否是T類(lèi)型,如果是,會(huì)將輸入值提取成T類(lèi)型的新變量X
•Var x 形式的 Var 模式(x是一個(gè)標(biāo)識(shí)符),它總是匹配的,并簡(jiǎn)單地將輸入值以它原本的類(lèi)型存入一個(gè)新變量X中。
這僅僅是個(gè)開(kāi)始 - 模式是一種新型的 C# 中的語(yǔ)言元素。未來(lái),我們希望增加更多的模式到 C# 中。
在 C#7.0,我們正在加強(qiáng)兩個(gè)現(xiàn)有的具有模式的語(yǔ)言結(jié)構(gòu):
•is 表達(dá)式現(xiàn)在具有一種右手側(cè)的模式,而不僅僅是一種類(lèi)型
•switch 語(yǔ)句中的 case 語(yǔ)句現(xiàn)在可以使用匹配模式,不只是常數(shù)值
在 C#的未來(lái)版本中,我們可能會(huì)增加更多的被用到的模式。
具有模式的 IS 表達(dá)式
下面是使用 is 表達(dá)式的示例,其中利用了常量模式和類(lèi)型模式:
public void PrintStars(object o) { if (o is null) return; // constant pattern "null" if (!(o is int i)) return; // type pattern "int i" WriteLine(new string('*', i)); }
正如你們看到,模式變量(模式引入的變量)和早前描述的輸出變量比較類(lèi)似,它們可以在表達(dá)式中間聲明,并在最近的范圍內(nèi)使用。就像輸出變量一樣,模式變量是可變的。
注:就像輸出變量一樣,嚴(yán)格范圍規(guī)則適用于Preview 4。
模式和 Try方法可以很好地協(xié)同:
if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }
具有模式的 Switch 語(yǔ)句
我們正在歸納 Switch 語(yǔ)句:
•可以設(shè)定任何類(lèi)型的 Switch 語(yǔ)句(不只是原始類(lèi)型)
•模式可以用在 case 語(yǔ)句中
•Case 語(yǔ)句可以有特殊的條件
下面是一個(gè)簡(jiǎn)單的例子:
switch(shape) { case Circle c: WriteLine($"circle with radius {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} square"); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} rectangle"); break; default: WriteLine("<unknown shape>"); break; case null: throw new ArgumentNullException(nameof(shape)); }
關(guān)于新擴(kuò)展的 switch 語(yǔ)句,有幾點(diǎn)需要注意:
•Case 語(yǔ)句的順序現(xiàn)在變得重要:就像 catch 語(yǔ)句一樣,case 語(yǔ)句的范圍現(xiàn)在可以相交,第一個(gè)匹配上的會(huì)被選中。此外,就像 catch 語(yǔ)句一樣,編譯器通過(guò)去除明顯不會(huì)進(jìn)入的 case 來(lái)幫助你。在此之前,你甚至不需要告訴判斷的順序,所以這并不是一個(gè)使用 case 語(yǔ)句的巨大的改變。
•默認(rèn)的語(yǔ)句還是最后被判斷:盡管 null 的 case 語(yǔ)句在最后語(yǔ)句之前出現(xiàn),它也會(huì)在默認(rèn)語(yǔ)句被選中之前被測(cè)試。這是與現(xiàn)有 Switch 語(yǔ)義兼容的。然而,好的做法通常會(huì)將默認(rèn)語(yǔ)句放到最后。
•Switch 不會(huì)到最后的 null 語(yǔ)句:這是因?yàn)楫?dāng)前 IS 表達(dá)式的例子具有類(lèi)型匹配,不會(huì)匹配到 null。這保證了空值不會(huì)不小心被任何的類(lèi)型模式匹配上的情況;你必須更明確如何處理它們(或放棄它而使用默認(rèn)語(yǔ)句)。
通過(guò)一個(gè) case 引入模式變量:標(biāo)簽僅在相應(yīng)的 Switch 范圍內(nèi)。
元組
這是一個(gè)從方法中返回多個(gè)值的常見(jiàn)模式。目前可選用的選項(xiàng)并非是最佳的:
•輸出參數(shù):使用起來(lái)比較笨拙(即使有上述的改進(jìn)),他們?cè)谑褂卯惒椒椒ㄊ遣黄鹱饔玫摹?/p>
•System.Tuple<...> 返回類(lèi)型:冗余使用和請(qǐng)求一個(gè)元組對(duì)象的分配。
•方法的定制傳輸類(lèi)型:對(duì)于類(lèi)型,具有大量的代碼開(kāi)銷(xiāo),其目的只是暫時(shí)將一些值組合起來(lái)。
•通過(guò)動(dòng)態(tài)返回類(lèi)型返回匿名類(lèi)型:很高的性能開(kāi)銷(xiāo),沒(méi)有靜態(tài)類(lèi)型檢查。
在這點(diǎn)要做到更好,C#7.0 增加的元組類(lèi)型和元組文字:
(string, string, string) LookupName(long id) // tuple return type { ... // retrieve first, middle and last from data storage return (first, middle, last); // tuple literal }
這個(gè)方法可以有效地返回三個(gè)字符串,以元素的形式包含在一個(gè)元組值里。
這種方法的調(diào)用將會(huì)收到一個(gè)元組,并且可以單獨(dú)地訪問(wèn)其中的元素:
var names = LookupName(id); WriteLine($"found {names.Item1} {names.Item3}.");
Item1 等是元組元素的默認(rèn)名稱(chēng),也可以被一直使用。但他們不具有描述性,所以你可以選擇添加更好的:
(string first, string middle, string last) LookupName(long id) // tuple elements have names
現(xiàn)在元組的接收者有多個(gè)具有描述性的名稱(chēng)可用:
var names = LookupName(id); WriteLine($"found {names.first} {names.last}.");
你也可以直接在元組文字指定元素名稱(chēng):
return (first: first, middle: middle, last: last); // named tuple elements in a literal
一般可以給元組類(lèi)型分配一些彼此無(wú)關(guān)的名稱(chēng):只要各個(gè)元素是可分配的,元組類(lèi)型就可以自如地轉(zhuǎn)換為其他的元組類(lèi)型。也有一些限制,特別是對(duì)元組文字,即常見(jiàn)的和告警錯(cuò)誤,如不慎交換元素名稱(chēng)的情況下,就會(huì)出現(xiàn)錯(cuò)誤。
Note:這些限制尚未在 Preview 4 中實(shí)現(xiàn)。
元組是值類(lèi)型的,它們的元素是公開(kāi)的,可變的。他們有值相等,如果所有的元素都是成對(duì)相等的(并且具有相同的哈希值),那么這兩個(gè)元組也是相等的(并且具有相同的哈希值)。
這使得在需要返回多個(gè)值的情況下,元組會(huì)非常有用。舉例來(lái)說(shuō),如果你需要多個(gè) key 值的字典,使用元組作為你的 key 值,一切會(huì)非常順利。如果你需要在每個(gè)位置都具有多個(gè)值的列表,使用元組進(jìn)行列表搜索,會(huì)工作的很好。
Note:元組依賴于一組基本類(lèi)型,卻不包括在 Preview 4 中。為了使該特性工作,你可以通過(guò) NuGet 獲取它們:
•右鍵單擊 Solution Explorer 中的項(xiàng)目,然后選擇“管理的NuGet包......”
•選擇“Browse”選項(xiàng)卡,選中“Include prerelease”,選擇“nuget.org”作為“Package source”
•搜索“System.ValueTuple”并安裝它。
解構(gòu)
消耗元組的另一種方法是將解構(gòu)它們。一個(gè)解構(gòu)聲明是一個(gè)將元組(或其他值)分割成部分并單獨(dú)分配到新變量的語(yǔ)法:
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration WriteLine($"found {first} {last}.");
在解構(gòu)聲明中,您可以使用 var 來(lái)聲明單獨(dú)的變量:
(var first, var middle, var last) = LookupName(id1); // var inside
或者將一個(gè)單獨(dú)的 var 作為一個(gè)縮寫(xiě)放入圓括號(hào)外面:
var (first, middle, last) = LookupName(id1); // var outside
你也可以使用解構(gòu)任務(wù)來(lái)解構(gòu)成現(xiàn)有的變量
(first, middle, last) = LookupName(id2); // deconstructing assignment
解構(gòu)不只是應(yīng)用于元組。任何的類(lèi)型都可以被解構(gòu),只要它具有(實(shí)例或擴(kuò)展)的解構(gòu)方法:
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
輸出參數(shù)構(gòu)成了解構(gòu)結(jié)果中的值。
(為什么它使用了參數(shù),而不是返回一個(gè)元組?這是為了讓你針對(duì)不同的值擁有多個(gè)重載)。
class Point { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } public void Deconstruct(out int x, out int y) { x = X; y = Y; } } (var myX, var myY) = GetPoint(); // calls Deconstruct(out myX, out myY);
這是一種常見(jiàn)的模式,以一種對(duì)稱(chēng)的方式包含了構(gòu)建和解構(gòu)。
對(duì)于輸出變量,我們計(jì)劃在解構(gòu)中加入通配符,來(lái)化簡(jiǎn)你不關(guān)心的變量:
(var myX, *) = GetPoint(); // I only care about myX
Note:通配符是否會(huì)出現(xiàn)在C#7.0中,這仍是未知數(shù)。
局部函數(shù)
有時(shí)候,一個(gè)輔助函數(shù)可以在一個(gè)獨(dú)立函數(shù)內(nèi)部起作用?,F(xiàn)在,你可以以一個(gè)局部函數(shù)的方式在其它函數(shù)內(nèi)部聲明這樣的函數(shù):
public int Fibonacci(int x) { if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x)); return Fib(x).current; (int current, int previous) Fib(int i) { if (i == 0) return (1, 0); var (p, pp) = Fib(i - 1); return (p + pp, p); } }
閉合范圍內(nèi)的參數(shù)和局部變量在局部函數(shù)的內(nèi)部是可用的,就如同它們?cè)?lambda 表達(dá)式中一樣。
舉一個(gè)例子,迭代的方法實(shí)現(xiàn)通常需要一個(gè)非迭代的封裝方法,以便在調(diào)用時(shí)檢查實(shí)參。(迭代器本身不啟動(dòng)運(yùn)行,直到 MoveNext 被調(diào)用)。局部函數(shù)非常適合這樣的場(chǎng)景:
public IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> filter) { if (source == null) throw new ArgumentNullException(nameof(source)); if (filter == null) throw new ArgumentNullException(nameof(filter)); return Iterator(); IEnumerable<T> Iterator() { foreach (var element in source) { if (filter(element)) { yield return element; } } } }
如果迭代器有一個(gè)私有方法傳遞給過(guò)濾器,那么當(dāng)其它成員意外的使用迭代器時(shí),迭代器也變得可用(即使沒(méi)有參數(shù)檢查)。此外,還會(huì)采取相同的實(shí)參作為過(guò)濾器,以便替換范圍內(nèi)的參數(shù)。
注意:在 Preview 4,局部函數(shù)在調(diào)用之前,必須被聲明。這個(gè)限制將會(huì)被松開(kāi),以便使得局部函數(shù)從定義分配中讀取時(shí),能夠被調(diào)用。
文字改進(jìn)
C#7.0 允許 _ 出現(xiàn),作為數(shù)字分隔號(hào):
var d = 123_456; var x = 0xAB_CD_EF;
你可以將 _ 放入任意的數(shù)字之間,以提高可讀性,它們對(duì)值沒(méi)有影響。
此外,C#7.0 引入了二進(jìn)制文字,這樣你就可以指定二進(jìn)制模式而不用去了解十六進(jìn)制。
var b = 0b1010_1011_1100_1101_1110_1111;
引用返回和局部引用
就像在 C# 中通過(guò)引用來(lái)傳遞參數(shù)(使用引用修改器),你現(xiàn)在也可以通過(guò)引用來(lái)返回參數(shù),同樣也可以以局部變量的方式存儲(chǔ)參數(shù)。
public ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; // return the storage location, not the value } } throw new IndexOutOfRangeException($"{nameof(number)} not found"); } int[] array = { 1, 15, -39, 0, 7, 14, -12 }; ref int place = ref Find(7, array); // aliases 7's place in the array place = 9; // replaces 7 with 9 in the array WriteLine(array[4]); // prints 9
這是繞過(guò)占位符進(jìn)入大數(shù)據(jù)結(jié)構(gòu)的好方法。例如,一個(gè)游戲也許會(huì)將它的數(shù)據(jù)保存在大型預(yù)分配的陣列結(jié)構(gòu)中(為了避免垃圾回收機(jī)制暫停)。方法可以將直接引用返回成一個(gè)結(jié)構(gòu),通過(guò)它的調(diào)用者可以讀取和修改它。
也有一些限制,以確保安全:
•你只能返回“安全返回”的引用:一個(gè)是傳遞給你的引用,一個(gè)是指向?qū)ο笾械囊谩?/p>
•本地引用會(huì)被初始化成一個(gè)本地存儲(chǔ),并且不能指向另一個(gè)存儲(chǔ)。
異步返回類(lèi)型
到現(xiàn)在為止,C# 的異步方法必須返回 void,Task 或 Task<T>。C#7.0 允許其它類(lèi)型以這種能從一個(gè)方法中返回的方式被定義,因?yàn)樗鼈兛梢砸援惒椒椒ū环祷氐姆绞絹?lái)定義其它類(lèi)型。
例如我們計(jì)劃建立一個(gè) ValueTask<T> 結(jié)構(gòu)類(lèi)型的數(shù)據(jù)。建立它是為了防止異步運(yùn)行的結(jié)果在等待時(shí)已可用的情境下,對(duì) Task<T> 進(jìn)行分配。對(duì)于許多實(shí)例中設(shè)計(jì)緩沖的異步場(chǎng)景,這可以大大減少分配的數(shù)量并顯著地提升性能。
Note:異步返回類(lèi)型尚未在 Preview 4 中提供。
更多的 expression bodied 成員:
expression bodied 的方法和屬性是對(duì) C# 6.0 的巨大提升。C# 7.0 為 expression bodied 事件列表增加了訪問(wèn)器,結(jié)構(gòu)器和終結(jié)器。
class Person { private static ConcurrentDictionary<int, string> names = new ConcurrentDictionary<int, string>(); private int id = GetId(); public Person(string name) => names.TryAdd(id, name); // constructors ~Person() => names.TryRemove(id, out *); // destructors public string Name { get => names[id]; // getters set => names[id] = value; // setters } }
Note:這些額外增加的 expression bodied 的成員尚未在 Preview 4 中提供。
這是社區(qū)共享的示例,而不是 Microsoft C# 編譯團(tuán)隊(duì)提供的,還是開(kāi)源的!
Throw 表達(dá)式
在表達(dá)式中間拋出一個(gè)異常是很容易的:只需為自己的代碼調(diào)用一個(gè)方法!但在 C#7.0 中,我們?cè)试S在任意地方拋出一個(gè)表達(dá)式:
class Person { public string Name { get; } public Person(string name) => Name = name ?? throw new ArgumentNullException(name); public string GetFirstName() { var parts = Name.Split(" "); return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!"); } public string GetLastName() => throw new NotImplementedException(); }
Note:Throw 表達(dá)式尚未在Preview 4中提供。
以上所述是小編給大家介紹的C#7.0中新特性匯總,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Unity UGUI的ContentSizeFitter內(nèi)容尺寸適應(yīng)器組件使用示例
這篇文章主要為大家介紹了Unity UGUI的ContentSizeFitter內(nèi)容尺寸適應(yīng)器組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08在C#中如何使用正式表達(dá)式獲取匹配所需數(shù)據(jù)
本文給大家分享C#中如何使用正式表達(dá)式獲取匹配所需數(shù)據(jù) ,非常實(shí)用,對(duì)正則表達(dá)式獲取匹配相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-03-03關(guān)于C#版Nebula客戶端編譯的問(wèn)題
這篇文章主要介紹了C#版Nebula客戶端編譯的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07C#中的文件路徑獲取函數(shù)和文件名字獲取函數(shù)小結(jié)
這篇文章主要介紹了C#中的文件路徑獲取函數(shù)和文件名字獲取函數(shù)小結(jié),本文講解了獲取絕對(duì)文件路徑、獲取文件名字、獲得包含 path 目錄信等內(nèi)容,需要的朋友可以參考下2015-01-01c#使用win32api實(shí)現(xiàn)獲取光標(biāo)位置
本文給大家匯總了2個(gè)使用C#實(shí)現(xiàn)獲取光標(biāo)位置的代碼,非常的簡(jiǎn)單實(shí)用,第二種方法更為全面,推薦給大家。2016-02-02