C7の新機胜ずVisual Studio“ 15” Preview 4で既にサポヌトされおいる機胜

Visual Studio“ 15” Preview 4のリリヌスにより、倚くの新しいC7機胜を詊すこずができたす。 C7の䞻な革新は、デヌタの操䜜を容易にし、コヌドを簡玠化し、パフォヌマンスを向䞊させるように蚭蚈されおいたす。 私自身は、Cは関数型蚀語に移行しおおり、タプルやパタヌンマッチングなどを远加しおいるず蚀いたす。 すべおの新機胜がプレビュヌ4で意図したずおりに機胜するわけではありたせん。これらの堎合、珟圚䜕を䜿甚できるか、および今埌どのように機胜するかが瀺されたす。 さあ、始めたしょう。

アりト倉数


out倉数の䜿甚は、珟圚のように簡単ではありたせん。 out匕数を䜿甚しおメ゜ッドを呌び出す前に、このメ゜ッドに枡される倉数を宣蚀する必芁がありたす。 通垞、これらの倀は宣蚀䞭にこれらの倉数に割り圓おられないため論理的です-メ゜ッドによっお匕き続き䞊曞きされたす、 varキヌワヌドは䜿甚できたせん。 タむプを瀺す倉数を宣蚀する必芁がありたす。

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

C7では、メ゜ッド呌び出しで倉数をすぐに宣蚀できるout倉数が远加されおいたす。

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

このような倉数のスコヌプは倖郚ブロックです。そのため、メ゜ッド呌び出しに続く匏で倉数を䜿甚できたす。

泚プレビュヌ4では、スコヌプに察しおより厳しい制限がありたす。out倉数は、定矩された匏内でのみ䜿甚できたす。 したがっお、䞊蚘の䟋が機胜するためには、次のリリヌスを埅぀必芁がありたす。

out倉数はメ゜ッド匕数ずしおの転送ず同じ匏で宣蚀されおいるため、コンパむラはその型を掚枬できたすこのメ゜ッドに競合するオヌバヌロヌドがない堎合。したがっお、型の代わりにvarキヌワヌドを䜿甚できたす。

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

出力匕数は、 Try ...メ゜ッドファミリで広く䜿甚されおいたす。戻り倀のブヌル倀は操䜜の成功を瀺し、出力匕数には結果の倀が含たれたす。

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

泚この䟋では 、 iは定矩されおいるifブロック内でのみ䜿甚されるため、プレビュヌ4ではこの䟋も機胜したす。

可胜な改善の1぀必ずしもC7に該圓するわけではありたせんは、埌で䜿甚されないoutパラメヌタヌの代わりにワむルドカヌド*を䜿甚するこずです。 䟋

 p.GetCoordinates(out int x, out *); //    x 

パタヌンマッチング


Cの7番目のバヌゞョンでは、 パタヌンの抂念が衚瀺されたす。これは、䞀般的な堎合、倉数が特定のパタヌンに䞀臎するかどうかを確認し、䞀臎する堎合はそこから情報を抜出できる構文構造です。

C7には次のテンプレヌトがありたす。


これはほんの始たりに過ぎず、将来的にはCで新しいテンプレヌトを確実に远加する予定です。 テンプレヌトをサポヌトするために、2぀の既存の蚀語構成が倉曎されたした。


将来的には、テンプレヌトを䜿甚するためのオプションをさらに远加する予定です。

あるテンプレヌト


定数パタヌンず型パタヌンの䞡方を䜿甚する簡単な䟋を考えおみたしょう。

 public void PrintStars(object o) { if (o is null) return; //   "null" if (!(o is int i)) return; //   "int i" WriteLine(new string('*', i)); } 

䟋からわかるように、テンプレヌト倉数テンプレヌトで宣蚀されたはout倉数ず同じスコヌプを持っおいるため、倖郚の可芖性ブロック内で䜿甚できたす。

泚プレビュヌ4では、テンプレヌト倉数および出力倉数に、より厳密な可芖性ルヌルが適甚されるため、この䟋は次のリリヌスでのみ機胜したす。

テンプレヌトずTryメ゜ッドは䞀緒に䜿甚できたす。

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

パタヌンず衚珟を切り替える


スむッチの䜿甚䟋が拡匵され、次のこずができるようになりたした。


次に䟋を考えおみたしょう。

 switch(shape) { case Circle c: WriteLine($"   {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} "); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} "); break; default: WriteLine("< >"); break; case null: throw new ArgumentNullException(nameof(shape)); } 

新しい高床なスむッチの次の機胜に泚意しおください。


caseで宣蚀されたテンプレヌト倉数のスコヌプはswitchステヌトメントです。

タプル


メ゜ッドから耇数の倀を返したい堎合がありたす。 珟圚利甚可胜な方法はどれも最適に芋えたせん


このタスクを簡玠化するために、タプルずタプルリテラルがC7に远加されたした。

 (string, string, string) LookupName(long id) //   -  { ... //   return (first, middle, last); //   } 

珟圚、このメ゜ッドはタプルに結合された3぀の行を返したす。 呌び出しコヌドは次のようにそれらを䜿甚できたす。

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

フィヌルド名Item1、Item2、...は各タプルのデフォルト名ですが、タプルにマヌゞされたデヌタをより適切な名前にするこずができたす。

 (string first, string middle, string last) LookupName(long id) //       

これで、タプル芁玠に次のようにアクセスできたす。

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

たた、芁玠名はタプルリテラルですぐに指定できたす。

 return (first: first, middle: middle, last: last); //      

芁玠の名前が䞀臎しない堎合、タプルを互いに割り圓おるこずができたす。䞻なこずは、芁玠自䜓を互いに割り圓おるこずができるずいうこずです。 将来的には、䞻にタプルリテラルに察しお制限が远加され、芁玠名が誀っおスワップされるなどの゚ラヌを通知したす。 Preview 4にはただそのような制限はありたせん。

タプルは意味のあるタむプであり、その芁玠は可倉のオヌプンフィヌルドです。 タプルは等しいかどうかを比范できたす。すべおの構成芁玠がペアで互いに等しいおよび同じハッシュコヌドを持぀堎合、2぀のタプルは等しいおよび同じハッシュコヌドを持぀。 この動䜜により、メ゜ッドから耇数の倀を返すだけでなく、タプルが䟿利になりたす。 たずえば、耇合キヌを持぀蟞曞が必芁な堎合は、タプルをキヌずしお䜿甚したす。 各䜍眮に耇数の倀があるはずのリストが必芁な堎合、タプルのリストも䜿甚したす。 翻蚳者から100の状況でタプルを䜿甚するためのガむドずしおこれを受け取らないでください。時には、いく぀かのプロパティを持぀単玔なクラスが意図をよりよく衚珟し、将来維持しやすくなりたす。

泚䜜業䞭のタプルは、ただプレビュヌ4にないタむプに䟝存しおいたすが、NuGetを䜿甚しおプロゞェクトに远加できたす「プレリリヌスを含める」を遞択し、「パッケヌゞ゜ヌス」ずしお「nuget.org」を指定するこずを忘れないでください、パッケヌゞSystem.ValueTupleず呌ばれたす 。

タプルの展開


タプルを䜿甚する別の方法は、タプルをアンパックするこずです。これは、芁玠を新しい倉数に割り圓おるこずで構成されたす。

 (string first, string middle, string last) = LookupName(id1); // deconstructing declaration WriteLine($" {first} {last}."); 

各倉数のタむプの代わりにvarキヌワヌドを䜿甚するこずもできたす。

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

たたは、括匧の前にvarを入れたす

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

タプルを既に宣蚀されおいる倉数にアンパックするこずもできたす。

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

タプルだけでなく、どのタむプでもアンパックできたす。 これを行うには、次の圢匏のメ゜ッドが必芁です。

 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(); //  Deconstruct(out myX, out myY); 

このアプロヌチにより、「察称」コンストラクタヌず展開メ゜ッドを䜜成できたす。
out倉数ず同様に、返されたパラメヌタヌの䞀郚を無芖するワむルドカヌドを远加する予定です。

 (var myX, *) = GetPoint(); //    myX 

泚 C7でワむルドカヌドが远加されるかどうかはただ䞍明です。

ロヌカル機胜


補助関数は、呌び出される1぀のメ゜ッド内でのみ意味をなす堎合がありたす。 これで、そのような関数をメ゜ッド内で宣蚀できたす。

 public int Fibonacci(int x) { if (x < 0) throw new ArgumentException("  !", 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); } } 

倖郚メ゜ッドの匕数ずそのロヌカル倉数は、ラムダ匏だけでなくロヌカル関数でも䜿甚できたす。

別の䟋ずしお、反埩子ずしお実装されたメ゜ッドを考えたす。 この堎合、そのようなメ゜ッドは通垞、匕数をチェックするために非反埩ラッパヌメ゜ッドを必芁ずしたす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; } } } } 

Iteratorメ゜ッドが通垞のプラむベヌトメ゜ッドである堎合、匕数をチェックせずに偶然に呌び出される可胜性がありたす。 さらに、圌はFilterメ゜ッドず同じ匕数を枡す必芁がありたす。

泚プレビュヌ4では、呌び出す前にロヌカル関数を宣蚀する必芁がありたす。 将来、この制限は緩和されたす。ロヌカル関数は、䜿甚されるすべおのロヌカル倉数に倀が割り圓おられた埌に呌び出すこずができたす。

リテラルの機胜匷化


C7では、数倀リテラルの区切り文字ずしお_を远加する機胜が远加されたした。

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

区切り蚘号は数倀間の任意の堎所に远加できたすが、倀には圱響したせん。
バむナリリテラルもC7に登堎したした。

 var b = 0b1010_1011_1100_1101_1110_1111; 

参照によるロヌカル倉数ず戻り倀


参照によっおメ゜ッドにパラメヌタヌを枡す refキヌワヌドを䜿甚だけでなく、参照によっおメ゜ッドからデヌタを返すこずができ、ロヌカル倉数ぞのリンクを保存するこずもできたす。

 public ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; //     ,      } } throw new IndexOutOfRangeException($"{nameof(number)}  "); } int[] array = { 1, 15, -39, 0, 7, 14, -12 }; ref int place = ref Find(7, array); //   ,   7   place = 9; //  7  9 WriteLine(array[4]); //  9 

これで、倧きなデヌタ構造の特定の堎所にリンクを転送するのが䟿利になりたす。 たずえば、ゲヌムでは、情報は構造の事前に割り圓おられた倧きな配列に含たれおいたすガベヌゞコレクションの䞀時停止を避けるため。 これで、メ゜ッドはこれらの構造の1぀ぞのリンクを返すこずができ、その助けを借りお、呌び出し元のコヌドはこの構造を読み取り、倉曎できたす。

リンクを安党に凊理するために、次の制限が導入されおいたす。


非同期メ゜ッドによっお返される型のリストの拡匵


今日たで、 非同期メ゜ッドはvoid 、 TaskたたはTask <T>のみを返すこずができたした。 C7では、非同期で返すこずもできる型を䜜成する機胜が導入されたした。 たずえば、 ValueTask <T>構造を䜜成しお、非同期操䜜の結果が既に利甚可胜な堎合にTask <T>オブゞェクトを䜜成しないようにするこずができたす。 たずえば、バッファリングが䜿甚される倚くの非同期シナリオでは、このアプロヌチによりメモリ割り圓おの数が倧幅に削枛され、パフォヌマンスが向䞊したす。

もちろん、タスクのようなオブゞェクトが圹立぀他の状況を思い぀くこずができたす。 これらの型を正しく䜜成するのは簡単な䜜業ではないため、倚数の開発者がそれらを䜜成するこずは期埅しおいたせんが、さたざたなフレヌムワヌクで有甚であり、呌び出しコヌドはTaskのようにawaitを単玔に䜿甚できるず考えおいたす。

泚プレビュヌ4では、これらのタむプはただ䜿甚できたせん。

匏ずしおのより倚くのクラスメンバヌ


C6に登堎する匏匏のボディメンバの圢匏のメ゜ッドずプロパティがよく䜿甚されたしたが、すべおのタむプのクラスメンバがそのように宣蚀できるわけではありたせん。 C7では、セッタヌ、ゲッタヌ、コンストラクタヌ、およびデストラクタヌのサポヌトが远加されたした。

 class Person { private static ConcurrentDictionary<int, string> names = new ConcurrentDictionary<int, string>(); private int id = GetId(); public Person(string name) => names.TryAdd(id, name); //  ~Person() => names.TryRemove(id, out *); //  public string Name { get => names[id]; //  set => names[id] = value; //  } } 

これは、コンパむラ開発チヌムではなく、コミュニティによっお远加された新しい機胜の䟋です やったヌ

泚プレビュヌ4では、これらのクラスメンバヌのサポヌトは利甚できたせん。

匏を投げる


匏の途䞭で䟋倖をスロヌするこずはそれほど難しくありたせん。それを行うメ゜ッドを呌び出すだけです。 ただし、C7では、匏の䞀郚ずしおthrowを䜿甚できるようになりたした。

 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では、このような匏はただ䜿甚できたせん。

おわりに


C7のリリヌスはただただ先ですが、今ではほずんどの新機胜を詊しお、Cず.Netが䞀般的にどこに移動しおいるかを理解できたす私にずっおは、Cは関数型蚀語からチップの䞀郚を取埗し、これによりコヌドが読みやすくなり、しかし、もちろん、枬定倀を知る必芁があるすべおの堎所で。

Source: https://habr.com/ru/post/J311112/


All Articles