您好,登錄后才能下訂單哦!
這篇文章主要介紹了C#7.0的新特性有哪些,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
C# 6.0 中,可以對成員方法和只讀屬性使用 Lambda 表達式,當時最郁悶的就是為什么不支持屬性的 set 訪問器。現在好了,不僅 set 方法器支持使用 Lambda 表達式,構造方法、析構方法以及索引都支持以 Lambda 表達式方式定義了。
class SomeModel { private string internalValue; public string Value { get => internalValue; set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value; } }
out
變量out
變量是之前就存在的語法,C# 7.0 只是允許它將申明和使用放在一起,避免多一行代碼。最直接的效果,就是可以將兩個語句用一個表達式完成。這里以一個簡化版的 Key
類為例,這個類早期被我們用于處理通過 HTTP Get/Post 傳入的 ID 值。
public class Key { public string Value { get; } public Key(string key) { Value = key; } public int IntValue { get { // C# 6.0,需要提前定義 intValue,但不需要初始化 // 雖然 C# 6.0 可以為只讀屬性使用 Lambda 表達式 // 但這里無法用一個表達式表達出來 int intValue; return int.TryParse(Value, out intValue) ? intValue : 0; } } }
而在 C# 7 中就簡單了
// 注意 out var intValue, // 對于可推導的類型甚至可以用 var 來申明變量 public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
用過 System.Tuple
的朋友一定對其 Item1
、Item2
這樣毫無語義的命名深感不爽。不過 C# 7.0 帶來了語義化的命名,同時,還減化了元組的創建,不再需要 Tuple.Create(...)
。另外,要使用新的元組特性和解構,需要引入 NuGet 包 System.ValueTuple
。
Install-Package System.ValueTuple
當然,元組常用于返回多個值的方法。也有些人喜歡用 out
參數來返回,但即使現在可以 out
變量,我仍然不贊成廣泛使用 out
參數。
下面這個示例方法用于返回一個默認的時間范圍(從今天開始算往前一共 7 天),用于數據檢索。
// 返回類型是一個包含兩個元素的元組 (DateTime Begin, DateTime End) GetDefaultDateRange() { var end = DateTime.Today.AddDays(1); var begin = end.AddDays(-7); // 這里使用一對圓括號就創建了一個元組 return (begin, end); }
調用這個方法可以獲得元組,因為定義的時候返回值指定了每個數據成員的名稱,所以從元組獲取數據可以是語義化的,當然仍然可以使用 Item1
和 Item2
。
var range = GetDefaultDateRange(); var begin = range.Begin; // 也可以 begin = range.Item1 var end = range.End; // 也可以 end = range.Item2
上面這個例子還可以簡化,不用 range
這個中間變量,這就用到了解構
var (begin, end) = GetDefaultDateRange();
這里創建元組是以返回值來舉例的,其實它就是一個表達式,可以在任何地方創建元組。上面的例子邏輯很簡單,可以用表達式解決。下面的示例順便演示了非語義化的返回類型申明。
// 原來的 (DateTime Begin, DateTime End) 申明也是沒問題的 (DateTime, DateTime) GetDefaultDateRange() => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
解構方法可以讓任何類(而不僅僅是元組)按定義的參數進行解構。而且神奇的是解構方法可以是成員方法,也可以定義成擴展方法。
public class Size { public int Width { get; } public int Height { get; } public int Tall { get; } public Size(int width, int height, int tall) { this.Width = width; this.Height = height; this.Tall = tall; } // 定義成成員方法的解構 public void Deconstruct(out int width, out int height) { width = Width; height = Height; } } public static class SizeExt { // 定義成擴展方法的解構 public static void Deconstruct(this Size size, out int width, out int height, out int tall) { width = size.Width; height = size.Height; tall = size.Tall; } }
下面是使用解構的代碼
var size = new Size(1920, 1080, 10); var (w, h) = size; var (x, y, z) = size;
Size
的構造方法還記得前面提到的構造方法可以定義為 Lambda 表達式嗎?下面是使用元組和 Lambda 對 Size 構造方法的改造——我已經醉了!
public Size(int width, int height, int tall) => (Width, Height, Tall) = (width, height, tall);
模式匹配目前支持 is
和 switch
。說起來挺高大上的一個名字,換個接地氣一點的說法就是判斷類型順便定義個具體類型的引用,有興趣還可以加再點額外的判斷。
對于 is
來說,就是判斷的時候順便定義個變量再初始化一下,所以像原來這樣寫的代碼
// 假設邏輯能保證這里的 v 可能是 string 也 可能是 int string ToString(object v) { if (v is int) { int n = (int) v; return n.ToString("X4"); } else { return (string) n; } }
可以簡化成——好吧,直接一步到位寫成表達式好了
string ToString(object v) => (v is int n) ? n.ToString("X4") : (string) v;
當然你可能說之前的那個也可以簡化成一個表達式——好吧,不深究這個問題好嗎?我只是演示
is
的模式匹配而已。
而 switch
中的模式匹配似乎要有用得多,還是以 ToString 為例吧
static string ToString(object v) { switch (v) { case int n when n > 0xffff: // 判斷類型,匹配的情況下再對值進行一個判斷 return n.ToString("X8"); case int n: // 判斷類型,這里 n 肯定 <= 0xffff return n.ToString("X4"); case bool b: return b ? "ON" : "OFF"; case null: return null; default: return v.ToString(); } }
注意一下上面第一個分支中 when
的用法就好了。
這已經是很接近 C/C++ 的一種用法了。雖然官方說法是這樣做可以解決一些安全性問題,但我個人目前還是沒遇到它的使用場景。如果設計足夠好,在目前又加入了元組新特性和解構的情況下,個人認為幾乎可以避免使用 out
和 ref
。
既然沒用到,我也不多說了,有用到的同學來討論一下!
這里有兩點增強,一點是引入了 0b
前綴的二進制數字面量語法,另一點是可以在數值字面量中任意使用 _
對數字進行分組。這個不用多數,舉兩個例就明白了
const int MARK_THREE = 0b11; // 0x03 const int LONG_MARK = 0b_1111_1111; // 0xff const double PI = 3.14_1592_6536
經常寫 JavaScript 的同學肯定會深有體會,局部函數是個好東西。當然它在 C# 中帶來的最大好處是將某些代碼組織在了一起。我之前在項目中大量使用了 Lambda 來代替局部函數,現在可以直接替換成局部函數了。Labmda 和局部函數雖然多數情況下能做同樣的事情,但是它們仍然有一些區別
對于 Lambda,編譯器要干的事情比較多。總之呢,就是編譯效率要低得多
Lambda 通過委托實現,調用過程比較復雜,局部函數可以直接調用。簡單地說就是局部函數執行效率更高
Lambda 必須先定義再使用,局部函數可以定義在使用之后。據說這在對遞歸算法的支持上會有區別
比較常用的地方是 Enumerator 函數和 async 函數中,因為它們實際都不是立即執行的。
我在項目中多是用來組織代碼。局部函數代替只被某一個公共 API 調用的私有函數來組織代碼雖然不失為一個簡化類結構的好方法,但是把公共 API 函數的函數體拉長。所以很多時候我也會使用內部類來代替某些私有函數來組織代碼。這里順便說一句,我不贊成使用 #region
組織代碼。
如果和 JavaScript 中 ES2017 的 async 相比,C# 中的 Task/Task<T>
就比較像 Promise
的角色。不用羨慕 JavaScript 的 async 支持 Promise like,現在 C# 的 async 也支持 Task like 了,只要實現了 GetAwaiter
方法就行。
官方提供了一個 ValueTask
作為示例,可以通過 NuGet 引入:
Install-Package System.Threading.Tasks.Extensions
這個 ValueTask
比較有用的一點就是兼容了數據類型和 Task:
string cache; ValueTask<string> GetData() { return cache == null ? new ValueTask<string>(cache) : new ValueTask<string>(GetRemoteData()); // 局部函數 async Task<string> GetRemoteData() { await Task.Delay(100); return "hello async"; } }
《〔譯〕 C# 7 的新特性》花了很大的篇幅來介紹 C# 7.0 的 9 個新特性,這里我根據項目經驗,通過實例對它們進行一個快速的介紹,讓大家能在短時間內了解它們。
總的來說,這些新特性使 C# 7.0 更容易以函數式編程的思想來寫代碼,C# 6.0 在這條路上已經做了不少工作, C# 7.0 更近一步!
C# 6.0 中,可以對成員方法和只讀屬性使用 Lambda 表達式,當時最郁悶的就是為什么不支持屬性的 set 訪問器。現在好了,不僅 set 方法器支持使用 Lambda 表達式,構造方法、析構方法以及索引都支持以 Lambda 表達式方式定義了。
class SomeModel { private string internalValue; public string Value { get => internalValue; set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value; } }
out
變量out
變量是之前就存在的語法,C# 7.0 只是允許它將申明和使用放在一起,避免多一行代碼。最直接的效果,就是可以將兩個語句用一個表達式完成。這里以一個簡化版的 Key
類為例,這個類早期被我們用于處理通過 HTTP Get/Post 傳入的 ID 值。
public class Key { public string Value { get; } public Key(string key) { Value = key; } public int IntValue { get { // C# 6.0,需要提前定義 intValue,但不需要初始化 // 雖然 C# 6.0 可以為只讀屬性使用 Lambda 表達式 // 但這里無法用一個表達式表達出來 int intValue; return int.TryParse(Value, out intValue) ? intValue : 0; } } }
而在 C# 7 中就簡單了
// 注意 out var intValue, // 對于可推導的類型甚至可以用 var 來申明變量 public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
用過 System.Tuple
的朋友一定對其 Item1
、Item2
這樣毫無語義的命名深感不爽。不過 C# 7.0 帶來了語義化的命名,同時,還減化了元組的創建,不再需要 Tuple.Create(...)
。另外,要使用新的元組特性和解構,需要引入 NuGet 包 System.ValueTuple
。
Install-Package System.ValueTuple
當然,元組常用于返回多個值的方法。也有些人喜歡用 out
參數來返回,但即使現在可以 out
變量,我仍然不贊成廣泛使用 out
參數。
下面這個示例方法用于返回一個默認的時間范圍(從今天開始算往前一共 7 天),用于數據檢索。
// 返回類型是一個包含兩個元素的元組 (DateTime Begin, DateTime End) GetDefaultDateRange() { var end = DateTime.Today.AddDays(1); var begin = end.AddDays(-7); // 這里使用一對圓括號就創建了一個元組 return (begin, end); }
調用這個方法可以獲得元組,因為定義的時候返回值指定了每個數據成員的名稱,所以從元組獲取數據可以是語義化的,當然仍然可以使用 Item1
和 Item2
。
var range = GetDefaultDateRange(); var begin = range.Begin; // 也可以 begin = range.Item1 var end = range.End; // 也可以 end = range.Item2
上面這個例子還可以簡化,不用 range
這個中間變量,這就用到了解構
var (begin, end) = GetDefaultDateRange();
這里創建元組是以返回值來舉例的,其實它就是一個表達式,可以在任何地方創建元組。上面的例子邏輯很簡單,可以用表達式解決。下面的示例順便演示了非語義化的返回類型申明。
// 原來的 (DateTime Begin, DateTime End) 申明也是沒問題的 (DateTime, DateTime) GetDefaultDateRange() => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
解構方法可以讓任何類(而不僅僅是元組)按定義的參數進行解構。而且神奇的是解構方法可以是成員方法,也可以定義成擴展方法。
public class Size { public int Width { get; } public int Height { get; } public int Tall { get; } public Size(int width, int height, int tall) { this.Width = width; this.Height = height; this.Tall = tall; } // 定義成成員方法的解構 public void Deconstruct(out int width, out int height) { width = Width; height = Height; } } public static class SizeExt { // 定義成擴展方法的解構 public static void Deconstruct(this Size size, out int width, out int height, out int tall) { width = size.Width; height = size.Height; tall = size.Tall; } }
下面是使用解構的代碼
var size = new Size(1920, 1080, 10); var (w, h) = size; var (x, y, z) = size;
Size
的構造方法還記得前面提到的構造方法可以定義為 Lambda 表達式嗎?下面是使用元組和 Lambda 對 Size 構造方法的改造——我已經醉了!
public Size(int width, int height, int tall) => (Width, Height, Tall) = (width, height, tall);
模式匹配目前支持 is
和 switch
。說起來挺高大上的一個名字,換個接地氣一點的說法就是判斷類型順便定義個具體類型的引用,有興趣還可以加再點額外的判斷。
對于 is
來說,就是判斷的時候順便定義個變量再初始化一下,所以像原來這樣寫的代碼
// 假設邏輯能保證這里的 v 可能是 string 也 可能是 int string ToString(object v) { if (v is int) { int n = (int) v; return n.ToString("X4"); } else { return (string) n; } }
可以簡化成——好吧,直接一步到位寫成表達式好了
string ToString(object v) => (v is int n) ? n.ToString("X4") : (string) v;
當然你可能說之前的那個也可以簡化成一個表達式——好吧,不深究這個問題好嗎?我只是演示
is
的模式匹配而已。
而 switch
中的模式匹配似乎要有用得多,還是以 ToString 為例吧
static string ToString(object v) { switch (v) { case int n when n > 0xffff: // 判斷類型,匹配的情況下再對值進行一個判斷 return n.ToString("X8"); case int n: // 判斷類型,這里 n 肯定 <= 0xffff return n.ToString("X4"); case bool b: return b ? "ON" : "OFF"; case null: return null; default: return v.ToString(); } }
注意一下上面第一個分支中 when
的用法就好了。
這已經是很接近 C/C++ 的一種用法了。雖然官方說法是這樣做可以解決一些安全性問題,但我個人目前還是沒遇到它的使用場景。如果設計足夠好,在目前又加入了元組新特性和解構的情況下,個人認為幾乎可以避免使用 out
和 ref
。
既然沒用到,我也不多說了,有用到的同學來討論一下!
這里有兩點增強,一點是引入了 0b
前綴的二進制數字面量語法,另一點是可以在數值字面量中任意使用 _
對數字進行分組。這個不用多數,舉兩個例就明白了
const int MARK_THREE = 0b11; // 0x03 const int LONG_MARK = 0b_1111_1111; // 0xff const double PI = 3.14_1592_6536
經常寫 JavaScript 的同學肯定會深有體會,局部函數是個好東西。當然它在 C# 中帶來的最大好處是將某些代碼組織在了一起。我之前在項目中大量使用了 Lambda 來代替局部函數,現在可以直接替換成局部函數了。Labmda 和局部函數雖然多數情況下能做同樣的事情,但是它們仍然有一些區別
對于 Lambda,編譯器要干的事情比較多。總之呢,就是編譯效率要低得多
Lambda 通過委托實現,調用過程比較復雜,局部函數可以直接調用。簡單地說就是局部函數執行效率更高
Lambda 必須先定義再使用,局部函數可以定義在使用之后。據說這在對遞歸算法的支持上會有區別
比較常用的地方是 Enumerator 函數和 async 函數中,因為它們實際都不是立即執行的。
我在項目中多是用來組織代碼。局部函數代替只被某一個公共 API 調用的私有函數來組織代碼雖然不失為一個簡化類結構的好方法,但是把公共 API 函數的函數體拉長。所以很多時候我也會使用內部類來代替某些私有函數來組織代碼。這里順便說一句,我不贊成使用 #region
組織代碼。
如果和 JavaScript 中 ES2017 的 async 相比,C# 中的 Task/Task<T>
就比較像 Promise
的角色。不用羨慕 JavaScript 的 async 支持 Promise like,現在 C# 的 async 也支持 Task like 了,只要實現了 GetAwaiter
方法就行。
官方提供了一個 ValueTask
作為示例,可以通過 NuGet 引入:
Install-Package System.Threading.Tasks.Extensions
這個 ValueTask
比較有用的一點就是兼容了數據類型和 Task:
string cache; ValueTask<string> GetData() { return cache == null ? new ValueTask<string>(cache) : new ValueTask<string>(GetRemoteData()); // 局部函數 async Task<string> GetRemoteData() { await Task.Delay(100); return "hello async"; } }
感謝你能夠認真閱讀完這篇文章,希望小編分享的“C#7.0的新特性有哪些”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。