<dl id="3wz6h"></dl><li id="3wz6h"></li>

      1. <dl id="3wz6h"></dl>

      2. <dl id="3wz6h"><ins id="3wz6h"></ins></dl>

            <dl id="3wz6h"></dl>

            <dl id="3wz6h"><ins id="3wz6h"></ins></dl>
            1. 
              
              <output id="3wz6h"><ins id="3wz6h"><nobr id="3wz6h"></nobr></ins></output>

              <li id="3wz6h"><ins id="3wz6h"></ins></li>
              
              

            2. <output id="3wz6h"><ins id="3wz6h"><nobr id="3wz6h"></nobr></ins></output>
              首頁»ASP.NET»C# 7.0 語言新特性

              C# 7.0 語言新特性

              來源:oschina 發布時間:2016-08-25 閱讀次數:

                下面是關于在C#7.0語言中計劃功能的說明。其中大部分功能在Visual Studio “15” Preview 4中能運行。現在是最好的試用時期,請記錄下你們的想法。

                C#7.0語言增加了許多的新功能,促使專注于數據消費,簡化代碼和性能。

                或許最大的特征是元組(tuples) ,使得它易于有多個結果,并從而簡化代碼是以對數據的形狀為條件的模式匹配。但也有許多其他的一些功能希望將它們結合起來,讓代碼運行更高效,更明確,從而獲得更多的創造性。如果有哪些運行不是你想要的或者有想改進的功能,在Visual Studio的窗口頂部使用“send feedback”功能將結果反饋給我們。在我所描述的許多功能在Preview 4還沒有辦法充分運行,根據用戶的反饋結果,我們將在發布最終版是增加些新的功能。而必須要指出的是,現有計劃中的一些功能在最終版也可能會有所改變或取消。

                如果你對這個功能設置感興趣并想學習它,在Roslyn GitHub site上可以找到許多的設計說明和相關討論。

                輸出(out)變量

                目前在C#中,使用out參數并不像我們想象中那么流暢。在使用out參數調用方法時,你首先必須聲明變量傳遞給它。雖然你通常不會初始化這些變量(他們將通過該方法后所有被覆蓋),也不能使用VAR來聲明他們,但是需要指定完整的類型:

              public void PrintCoordinates(Point p)
              {    int x, y; // have to "predeclare"
                  p.GetCoordinates(out x, out y);
                  WriteLine($"({x}, {y})");
              }

                在C#7.0,我們增加了Out變量,作為out參數傳遞的點來聲明一個變量權:

              public void PrintCoordinates(Point p)
              {
                  p.GetCoordinates(out int x, out int y);
                  WriteLine($"({x}, {y})");
              }

                需要注意的是,變量是在封閉塊范圍內,所以后續可以使用它們。大多數類型的語句不建立自己的適用范圍,因此out變量通常在聲明中被引入到封閉范圍。

              :在Preview 4中,適用范圍規則更為嚴格:out變量的作用域為它們在聲明的說法。因此,上面的例子不會在后續的版本中使用。

                由于out變量直接聲明作為參數傳遞給out參數,編譯器通常可以告知類型(除非有沖突的過載)。所以這是很好用VAR,而不是一個類型來聲明它們:

              p.GetCoordinates(out var x, out var y);

                out參數的一個常見的用途是Try...模式,其中out參數一個boolean return表示成功,out參數進行得到的結果:

              public void PrintStars(string s)
              {    if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }    else { WriteLine("Cloudy - no stars tonight!"); }
              }

              :Preview 4處理的比較好的地方在于只是用if語句定義它。

                計劃允許“wildcards”作為out參數以及在*的形式,忽視不重要的out參數:

              p.GetCoordinates(out int x, out *); // I only care about x

              注:wildcards能否把它變成C#7.0還是個未知數。

                模式匹配

                C# 7.0 引入了模式的概念,抽象地說,這是一種語法成分可以用來測試一個值是否有一個一定的“形”以及在它起作用時從值里面獲取到的額外信息。

                下面是 C# 7.0 中關于模式的例子:

              • c 的常量模式(c 是C#中的一個常量表達式),用于測試輸入的參數是否和 c 相等

              • T x 的類型模式(T 是一個類型,x 是一個標識符),用于測試輸入的參數是否有類型 T,如果有,提取輸入參數的值到一個 T 類型的新 x 變量中。

              • var x 變量模式(x 是一個標識符),通常會匹配并簡單地將輸入參數的值放進一個新變量 x 中

                這是個開始,模式是一種新的 C# 語言元素,而且我們將來可以把它們更多地增加到 C# 中。

                在 C# 7.0 中,我們正在使用模式以增強兩種已存在的語言結構:

              • is 表達式現在在右邊可以有一個模式,而不只是一個類型

              • case 子句在 switch 語句中現在可以通過模式匹配,而不僅僅是常量值

                在將來的C#中,我們或許會增加更多能使用模式的地方。

                帶模式的 Is 表達式

                這是一個使用帶有常量模式和類型模式的 is 表達式的例子:

              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));
              }

                正如你所看到的,模式變量(變量通過模式引入)與先前描述的 out 變量有些類似,他們可以在表達式中被聲明,而且可以在它們最近的周圍范圍內被使用。也像 out 變量那樣,模式變量是易變的,

              : 就像 out 變量一樣,嚴格的范圍規則適用于 Preview 4.

                模式和 Try 方法通常會一起出現:

              if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }

                帶模式的 Switch 語句

                我們正在泛化 switch 語句,因此:

              • 你可以在任何類型上使用 switch(不僅僅是原始類型)
              • 可以在 case 子句中使用模式
              • Case 子句可以擁有額外的條件

                這里是一個簡單的例子:

              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));
              }

                有幾件關于這個新擴展的 switch 語句的事需要注意:

              • case 子句的順序現在很重要:就像 catch 子句,case 子句不再是必然不相交的,第一個子句匹配的將被選擇。因此這里重要的是上面代碼中 square case 比 rectangle case 來得要早。也是和 catch 子句一樣,編譯器會通過標記明顯不能到達的情況來幫助你。在這之前,你永遠無法知道評價的順序,所以這不是一個重大改變的特性。
              • 默認子句總是最后被評價:即使上面代碼中 null 子句是最后才來,它會在默認子句被選擇前被檢查。這是為了與現有 switch 語義相兼容。然而,好的做法通常會讓你把默認子句放到最后。
              • null 子句在最后不可到達:這是因為類型模式遵循當前的 is 表達式的例子并且不會匹配空值。這保證了空值不會偶然被任何的類型模式捎來第一搶購。你必須更明確如何處理它們(或為默認子句留下他們)。

                通過 case ...: 標簽引入的模式變量僅存在于相對應的 switch 部分的范圍內。

                元組

                這是常見的希望從一個方法返回多個值的做法。目前可用的選項不是最佳的:

              • Out 參數。使用笨拙(即便有上面描述到的提升),它們不使用異步的方法運行。
              • System.Tuple<...>  返回類型。使用累贅并且需要一個元組對象的分配。
              • 為每個方法定制傳輸類型:大量的代碼為了類型開銷的目的僅是臨時收集一些值
              • 匿名類型通過返回一個 dynamic 返回類型。高性能開銷并且沒有靜態類型檢查。

                為了在這方面做得更好,C# 添加了tuple types  tuple literals:

              (string, string, string) LookupName(long id) // tuple return type
              {
                  ... // retrieve first, middle and last from data storage
                  return (first, middle, last); // tuple literal
              }

                這個方法目前有效地返回三個字符串,將其作為元素在元組類型里包裹起來。

                方法的調用者將會接受到一個元組,并且可以逐一訪問元素。

              var names = LookupName(id);
              WriteLine($"found {names.Item1} {names.Item3}.");

                Item1 等等,是元組元素的默認名字,并能夠經常被使用。但它們不是太好描述的,因此你可以選擇性地添加更好的一個。

              (string first, string middle, string last) LookupName(long id) // tuple elements have names

                現在元組的接受者擁有更多的可描述的名字用于運行:

              var names = LookupName(id);
              WriteLine($"found {names.first} {names.last}.");

                你也可以在 tuple literals 中直接指定名字:

              return (first: first, middle: middle, last: last); // named tuple elements in a literal

                通常來說,你可以互相分配元組類型無關的名字,只要獨立的元素是可以被分配的,元組類型會自如 轉換成其他元組類型。特別是對于 tuple literals ,存在一些限制,這會警告或提示在常見的錯誤的情況下提示,例如偶然交換元素的名字。

              注意:這些限制還沒在 Preview 4 中實現

                元組是值類型,而且他們的元素只是公開、易變的域。他們的值相等,代表這兩個元組是相等的(都有相同的哈斯碼)如果它們的元素都結對匹配(都有相同的哈斯碼)。

                這使得元組對于在多種返回值下的很多情況十分有用。舉例來說,如果你需要一個有多種鍵的詞典,使用元組作為你的鍵,然后一切東西就會如常工作。如果你需要在每個位置有一個有多種值的列表,使用元組,查找列表等等,程序會正常運行。

              注意:元組依賴一系列底層類型,它們在 Preview 4 中不被引入。為了將來的工作,你可以通過 NuGget 輕易獲取它們: 在 Solution Explorer 中右鍵點擊項目,并選擇“Manage NuGet Packages…” 選擇“Browse”選項卡,檢查“Include prerelease” 并且選擇“nuget.org”作為“Package source” 搜索“System.ValueTuple”并安裝它

                解構

                另一種消除元組(tuple)的方法是解構元組。通過一個解構聲明語法,把一個元組(或者其他的值)拆分為幾部分,并且重新定義為新的變量。

              (string first, string middle, string last) = LookupName(id1); // deconstructing decla
              rationWriteLine($"found {first} {last}.");

                在解構中可采用var關鍵字:

              (var first, var middle, var last) = LookupName(id1); // var inside

                或者把var關鍵字提取出來,在括號外:

              var (first, middle, last) = LookupName(id1); // var outside

                你也可以通過解構賦值來解構一個現有變量:

              (first, middle, last) = LookupName(id2); // deconstructing assignment

                不僅僅元組可以被解構,任何類型都可以被解構,只要有一個對應的(實體或者擴展)解構方法:

              public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }

                輸出參數由解構之后的結果值構成。

                (為什么采用數據參數代替返回一個元組?這樣,你可以重載多個不同的數值)

              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);

                這將成為一種常見模式,包含析構函數和“對稱”解析:

                針對輸出變量,我們計劃在解構中允許使用“通配符”:

              (var myX, *) = GetPoint(); // I only care about myX
                 注:仍然還沒有確定是否將通配符引入C# 7.0中。

                局部函數

                有時,一個輔助函數只在一個使用它的單一方法內部有意義。現在你可以在其他功能體內部聲明這些函數作為一個局部函數:

              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);
                  }
              }

                參數和閉合區間局部變量可用在局部函數內,類似lambda表達式。

                舉一個例子,方法實現迭代器通常需要嚴格檢查調用時非迭代器封裝方法。(迭代器本身沒有運行,只到調用MoveNext 才會運行)。局部函數在這種情況下是完美的:

              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; }
                      }
                  }
              }

                如果迭代器是一個私有方法的下一個過濾器,它將有可能被其他成員不小心使用(沒有參數檢查)。此外,作為過濾器,它將需要采取所有的相同的參數,而不是指定域內的參數。

                 注:在Preview 4版本中,本地函數必須在它們被調用之前聲明。這個限制將被放松,能調用讀取直接賦值的局部變量。

                Literal 改進

                C# 7.0 允許使用“_”作為數字分隔符在數字literals中:

              var d = 123_456;
              var x = 0xAB_CD_EF;

                你可以把它放在你想要的位置,提升可讀性。這樣對數值沒有任何影響。

                此外,C# 7.0也介紹了二進制literals,這樣你可以直接指定二進制模式而不必知道十六進制符號。

              var b = 0b1010_1011_1100_1101_1110_1111;

                Ref 返回和本地

                就像你可以通過reference(用ref修飾符)在C#中傳遞東西,您現在可以通過reference return 他們,并通過 reference將它們存儲在局部變量中。

              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

                這對繞過占位符成為大數據結構是非常有用的。舉例來說,一個游戲可能會在一個大的預分配數組結構中保存其數據(為避免垃圾收集暫停)。Methods 可以直接返回一個 reference 到這樣一個結構,且通過調用者可以讀取和修改它。

                這里有一些限制,以確保這是安全的:

              • 你可以只返回 refs 那些是 “安全返回(safe to return)”的:那些被傳遞給你的,和那些點到對象的字段。

              • Ref locals被初始化為某一存儲位置,并且不能突變到指向另一個。

                廣義異步返回類型

                截至目前為止,在C#調用異步方法必須要返回void,Task或Task<T>。C#7.0允許以這樣的方式來定義其它類型,從而使它們可以從異步方法返回。

                例如,我們計劃有一個ValueTask<T>結構類型。它是建立在預防Task<T> 對象的分配時,異步操作的結果是已在可等候的時間的情況下。對于很多異步場景,比如以涉及緩沖為例, 這可以大大減少分配的數量,并使性能有顯著提升。

                這里有許多其他的方法可以讓您想象自定義“task-like”類型是有用的。它不會是簡單的正確創建,所以我們不要指望大多數人推出自己的,但他們很可能將會開始在框架和API展現出來,然后調用方可以返回并await他們今天做任務(Tasks)的方式。

                  注:廣義異步返回類型尚未應用在預覽4。    

                更多 Expression-bodied 方法

                Expression-bodied 方法、屬性等都是C# 6.0的重大突破,但并不允許他們在各種各樣的member中使用,C#7.0添加了訪問器、構造函數和終結器等,使更多member可以使用Expression-bodied 方法:

              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
                  }
              }
              注:這些額外的Expression-bodied 方法還沒有工作在預覽4。

                這是一個由社區貢獻的特征,而非微軟C#團隊。并且,開源!

                在表達式的中間拋出一個異常是很容易的,只需要為你自己調用一個方法,但在C#7.0中我們允許在一些地方直接拋出表達式:

              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();
              }
                注:拋出表達式還未在預覽4工作。

                本文地址:http://www.oschina.net/translate/whats-new-in-csharp-7-0

                原文地址:https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

              QQ群:WEB開發者官方群(515171538),驗證消息:10000
              微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
              提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
              C#
              網友評論(共0條評論) 正在載入評論......
              理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
              登錄會員中心
              云南十一选往期