Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

C# 8.0 Preview in Visual Studio 2019 (16.0)

3.108 Aufrufe

Veröffentlicht am

https://connpass.com/event/122145/ にて登壇。

先日リリースされたVisual Studio 2019には、C# 8.0のプレビュー版が入っています。本日は、プレビューリリースの背景や、プレビュー機能の有効化方法、今現在でも安定して使えそうな機能の紹介などを行います。

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

C# 8.0 Preview in Visual Studio 2019 (16.0)

  1. 1. C# 8.0 Preview in Visual Studio 2019 (16.0) 岩永 信之
  2. 2. 今日の話 • C# 8.0の現状 • Visual Studio 2019 (16.0)はRTMだけど、C# 8.0はプレビュー • プレビュー機能の有効化方法 • プレビューでも安定している機能としてない機能がある • どれが安定していて、どれが安定していないか • 主要なものを中心に新機能紹介
  3. 3. 時期的な話
  4. 4. スケジュール感(過去2年分くらい) VS 15.0 VS 15.3 C# 7.1 VS 15.5 C# 7.0 .NET Core 2.0 VS 15.1 VS 15.2 VS 15.4 C# 7.2 VS 15.6 VS 15.7 C# 7.3 .NET Core 2.1 VS 15.9VS 15.8 .NET Core 2.2 VS 16.0 C# 8.0 Preview .NET Core 3.0 C# 8.0 (今年後半を予定)(今ここ)
  5. 5. ちょっと延び気味? • まあ、メジャー リリースなので • 元々間隔広め • C# 7.Xの時、しんどかったのかも? • 7.2とか7.3辺り、バグが多かったから… • 「プレビュー」という段階を経たい • 機能盛りすぎかも • スケジュール管理の仕方次第では「.NET Core 2.3」と「C# 7.4」が あってもよかった疑惑 • といっても今更区切る余裕はない • .NET Coreの方は切りどころが難しそう • C#の方には切りどころあったと思う • ただ、.NET Coreと足並み揃えたそう
  6. 6. C#プレビュー • C# 7.2のとき • C#チーム「プレビューとして出したい」 • Visual Studio「有効化しちゃった」 • C# 8.0では • ちゃんと、プレビューを使うかどうか選択式に • LangVersion (C#のバージョン指定オプション)の挙動も変更 • 元々あんなにハイペースでリリース するつもりではなさそうだった • 実際、結構バグが多かった
  7. 7. プレビュー機能の有効化
  8. 8. プレビュー機能の有効化方法 • Visual Studio 16.0でC# 8.0を使う方法 • 言語バージョン(LangVersion)を明示 • 8.0の明示 • 「Preview」指定 • プレビューのSDKを使用 • プレビューな.NET Core/.NET Standardをターゲットにすると、既定動作がC# 8.0 • netcoreapp3.0 (.NET Core 3.0) • netstandard2.1 (.NET Standard 2.1) • Visual Studio側のオプション指定も必要
  9. 9. 言語バージョン (Visual StudioのUI) プロジェクトのプロパティ Build Advanced... Language version 場所 指定する値 • 8.0 (beta) • preview of next C# version バージョンを明示 プレビューを含む最新版
  10. 10. 言語バージョン (LangVersion) • C#コンパイラー オプション • csproj設定 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <LangVersion>preview</LangVersion> </PropertyGroup> </Project> csc –langversion:preview
  11. 11. LangVersionに関する変更 • 今まで • default : 最新のメジャー バージョン • latest : 最新のマイナー バージョン • 新ルール • default : 最新のマイナー バージョン(latestと同じ) • latest : 最新のマイナー バージョン(正式リリースのみ) • latestMajor : 最新のメジャー バージョン • preview : (プレビューを含めた)最新バージョン • 既定動作(未指定時の挙動、default)がlatestと同じに • latestMajor、previewを新設
  12. 12. プレビューなSDK • C# 8.0は.NET Core 3.0と同時リリース予定 • .NET Core 3.0にとってはC# 8.0は「プレビュー」ではない • TargetFrameworkを.NET Core 3.0にするとC# 8.0がdefaultに • .NET Standard 2.1でもC# 8.0がdefault • TargetFrameworkを変えるだけでSDK自体が変わる <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> </Project> ターゲットを.NET Core 3.0にするだけで、 C# 8.0が有効化される
  13. 13. プレビューなSDK (Visual Studioの設定) • Visual Studio 2019上だとオプション指定が必要
  14. 14. C# 8.0の現状と概要
  15. 15. 安定度 • C# 8.0はプレビューという段階を経たい • 「C# 7.4」があり得たかもしれないくらい、機能ごとに: • 安定度にばらつきあり • .NET Core 3.0への依存の有無あり • C#チーム自身のドッグフーディング(Roslynへの適用)でも 「この機能なら現時点でも使っていい」区分あり※ ※ 「Enable C# 8.0 in the code base」参照
  16. 16. • Visual Studio 16.0の時点で 凡例 〇 △ △ ◎ × … 割と安定。大きな変更はなさそう … 割と不安定。使えるけど少し注意が必要 … 不安定。使えるけど、だいぶ人柱 … 安定。今後そんなに変更もなさそう … 未実装。.NET Core 3.0正式リリースまでには実装されるはず
  17. 17. 割と安定してそうな機能 • 再帰パターン • switch式 • usingの改善 • using変数宣言 • パターン ベースusing • 静的ローカル関数 • ローカル関数での引数のシャドーイング • その他こまごま • null合体代入 • @$ (順序緩和) この辺りは全部「Roslyn内 で使っていいもの」扱い ◎ 〇 16.1で変更予定あり(演算子の優先度) 〇 16.1で変更予定あり(値型の時の挙動)
  18. 18. 不安定な機能 • .NET Core 3.0依存なものは多少怖い (依存先がプレビューな以上、これから変更が掛かる可能性がある) • 非同期ストリーム • await/yield return混在 • await foreach/await using • インデックス範囲構文(Index型/Range型) • 単純にタスク山積みで不安定なやつ • null許容参照型 使っていいけどpublicな ところでは避けて まだ使わない △ △ まだ使わない
  19. 19. そもそもVS2019時点で実装がない(1) • 16.1で有効化されそうなもの • インターフェイスのデフォルト実装 • unmanaged generic structs • 分解右辺のdefault • readonly関数メンバー ×
  20. 20. そもそもVS2019時点で実装がない(2) • 予定にはあるけど本当に実装するのか怪しい • caller expression attribute • target-typed new • generic attributes • stackalloc in nested contexts • refとpartialの順序緩和 × さすがに今日は 完全にノータッチ 詳しくはLanguage Feature Statusを参照
  21. 21. 大きなものだけまとめ • 再帰パターン • switch式 • usingの改善 • 非同期ストリーム • インデックス範囲構文 • null許容参照型 • インターフェイスのデフォルト実装 ◎ 〇 ◎ △ △ △ × 気持ち的には「C# 7.4」 くらいの感覚で使える メジャー アップデート にふさわしい大型機能
  22. 22. ちょっと将来の機能紹介
  23. 23. null許容参照型(NRT) • nullable reference type、略してNRT • 参照型でもnullの有無の区別を付けたい • ? を付けたときだけnullを認める △ // null を意図していないメソッド int M1(string s) => s.Length; // null を意図しているメソッド int? M2(string s) => s?.Length; これまで(C# 7.3以前) 区別がつかない // null を意図していないメソッド int M1(string s) => s.Length; // null を意図しているメソッド int? M2(string? s) => s?.Length; これから(C# 8.0以降、オプション指定) nullを認めない nullを認める
  24. 24. NRTは破壊的変更 • 既存コードの意味が変わる(破壊的変更) • 単に参照型Tと書いたとき • 今まで : nullがあり得た • これから : nullを渡そうとすると警告 • なので、オプションを明示した時だけ有効に • C# ソースコード中に#nullable • csproj中にNullableContextOptions △ #nullable enable <PropertyGroup> <NullableContextOptions>Enable</NullableContextOptions> </PropertyGroup>
  25. 25. NRTは空前絶後の変更 • オプションの有無で文法が変わる • C#史上初だし、おそらくこの先も二度とない予定 • C#は「昔書いたコードは今のバージョンでも動く」が原則 • 文法の「分岐」も好まない • 原則を破ってでも入れたい修正 • それほど「billion-dollar mistake※」が大きい • エラーにはしない • 全部「警告」 • 警告のもみ消し手段も用意 • #nullable disable • 後置き ! 演算子 △ ※ null参照を最初に発明した人本人が「10億ドルにも相当する私の誤りだ」と明言
  26. 26. インターフェイスのデフォルト実装(DIM) • default interface method、略してDIM • インターフェイス内の関数メンバーに実装を持てるように • インターフェイスに後からメンバーを足せるように • Java/Swiftとの相互運用(Xamarin)でも必要 × interface I { // 普通の(実装を持たない)メンバー int M1(); // 実装を持つメンバー public int M2() => 0; } インターフェイスを実装している 既存のクラス側に変更を求めない
  27. 27. DIMはランタイム側の変更が必要 • C#コンパイラーだけではできない機能 • TargetFramework によっては使えない • 現状では.NET Core 3.0のみが対応 • WPFとかも.NET Core 3.0対応するので、必要なら移行を • Xamarinはすぐに対応するはず • Java/Swift相互運用のために必要 • 対応しているランタイムにはRuntimeFeatureクラス※に DefaultImplementationsOfInterfacesプロパティがある × ※ System.Runtime.CompilerServices名前空間
  28. 28. ランタイム側の変更は久しぶり • .NET Framework 2.0 (2005年)以来の「ランタイム側の修正」 • .NET Coreが落ち着いたからできた • やっと「移植で手一杯」のフェーズが終わった • 他にも、Hardware Intrinsics※とか、ランタイム特殊対応が増えた • いろいろなインストール形態ができるからこそ • OS全体に影響を及ぼすようなインストール形態では怖くてできない • .NET Coreなら、OS全体/アプリ単位を選べる × ※ 特定CPU向けの専用命令を出力できるようにする仕組み
  29. 29. 今使えそうな機能紹介
  30. 30. 安定してて有用そうなのを紹介 • switch式 • 再帰パターン • using変数宣言/パターン ベースusing • 静的ローカル関数 • ローカル関数での引数のシャドーイング • null合体代入 • 非同期ストリーム ◎ 〇 ◎ △ ◎ ◎ ◎
  31. 31. switch式 • これまで(switchはステートメントだけ) int y; switch (e) { case 明治: y = 1868; break; case 大正: y = 1912; break; case 昭和: y = 1926; break; case 平成: y = 1989; break; case 令和: y = 2019; break; default: throw new InvalidOperationException(); } // y を使って何か • caseとか代入とかbreakとかが並んでつらい • ステートメントなので書ける場所に制約あり 〇
  32. 32. switch式 • switchを式で書けるように(後置き switch + =>) var y = e switch { 明治 => 1868, 大正 => 1912, 昭和 => 1926, 平成 => 1989, 令和 => 2019, _ => throw new InvalidOperationException() }; // y を使って何か 〇
  33. 33. switc式(注意点) • 優先度が変わる • a + x switch { ... } みたいなのの意味が変わる • 今 : 比較演算子(< とか <= とか)と同じ辺り(かなり低め) • 予定 : 単項演算子の直後(かなり高め) • 型推論強化 • 代入先からの推論 or • 共通基底推論 A x = y switch {… } を A 扱い AとBに共通基底Baseがあるとき x switch { … => new A(), … => new B(), … } を Base 扱い 〇
  34. 34. 再帰パターン • 例としてこんな型があるとして • プロパティ2個 • プロパティを初期化するコンストラクター • プロパティから値を取り出すDeconstruct ◎ readonly struct Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); public void Deconstruct(int x, int y) => (x, y) = (X, Y); }
  35. 35. 再帰パターン • 使えるパターンが増えた • 対称性 ◎ static int M(object obj) => obj switch { Point _ => 1, _ => 0, }; こういうのはC# 7.0からある static int M(object obj) => obj switch { Point(1, 2) => 1, Point { X: 2, Y: 3 } => 2, Point _ => 1, _ => 0, }; 位置パターン(Deconstructを利用) プロパティ パターン 破棄パターン var p = new Point(1, 2) p is Point(1, 2) var p = new Point(x: 1, y: 2) p is Point(x: 1, y: 2) var p = new Point { X = 1, Y = 2) p is Point { X: 1, Y: 2 } コンストラクター/初期化子 位置パターン/プロパティ パターン
  36. 36. 再帰パターン • 名前通り、再帰的に書ける • 型が自明の時には () や {} だけで書ける • () と {} の混在もできる ◎ x is Line { A: Point (1, var a), B: Point (2, _) } var p = new Point(1, 2); p is (1, 2) p is (1, _) { Y: 2 }
  37. 37. 再帰パターン • タプルとの組み合わせが結構有効 ◎ static int CompareTo(int? x, int? y) => (x, y) switch { (null, null) => 0, ({}, null) => 1, (null, {}) => -1, ({} x1, {} y1) => x1.CompareTo(y1), };
  38. 38. using変数宣言 • 変数宣言時にusingを付けると、Disposeが呼ばれる • 変数のスコープがそのままusingのスコープになる ◎ using var file = File.OpenWrite("out.txt"); file.Write(data); { using var x = someDisposable; ... ... } ... ... Disposeが呼ばれるのはここ
  39. 39. using変数宣言 • 変数宣言時にusingを付けると、Disposeが呼ばれる • うかつに使うとDisposeのタイミングが遅れるので注意 ◎ using (var file = File.OpenWrite("out.txt")) { file.Write(data); } // この後だいぶ長い処理 こういうのをusing変数宣言に書き換えると Dispose呼び出しが遅れてまずい
  40. 40. using変数宣言 • 変数宣言時にusingを付けると、Disposeが呼ばれる • うかつに使うとDisposeのタイミングが遅れるので注意 • 必要な範囲だけメソッド抽出してしまえば気兼ねなく使える ◎ using var file = File.OpenWrite("out.txt"); file.Write(data); // この後だいぶ長い処理 ダメ!絶対! static void M(ReadOnlySpan<byte> data) { using var file = File.OpenWrite("out.txt"); file.Write(data); }
  41. 41. パターン ベースusing • usingステートメントで使える条件が緩和された • 今まで: IDisposableインターフェイスの実装が必須 • C# 8.0 : ref structに限り、IDisposable実装がなくてもOK ◎ struct Disposable : IDisposable { public void Dispose() { } } 今までは必須 ref struct Disposable : IDisposable { public void Dispose() { } } ref structにインターフェイス実装 はできない(コンパイル エラー) ref struct Disposable { public void Dispose() { } } なので、パターンさえ満たせば usingステートメントで使えるように修正
  42. 42. 静的ローカル関数 • ローカル関数外の変数のキャプチャ ◎ static void M(int a) { // 外の変数・引数を使ってる(キャプチャしてる) int f1(int x) => a * x; // 使ってない int f2(int x, int y) => x * y; } • キャプチャの有無で性能が変わる • 大体、無い方が性能がよくなる • 本当に意図的なキャプチャなのか • 実は避けれたのにミスしてないか
  43. 43. 静的ローカル関数 • ローカル関数外の変数のキャプチャ • static修飾することで、キャプチャしないことを明示できる ◎ static void M(int a) { // 外の変数・引数を使ってる(キャプチャしてる) static int f1(int x) => a * x; // 使ってない static int f2(int x, int y) => x * y; } キャプチャしちゃってると コンパイル エラーに キャプチャしていないことを宣言
  44. 44. 引数のシャドーイング • ローカル関数外の変数と同名の変数を使えるように • staticを付けておけば外の変数と混ざることはない ◎ static void M(int a) { // 外の変数・引数を使ってる(キャプチャしてる) int f1(int x) => a * x; // 使ってない static int f2(int a, int y) => a * y; } 外にある変数と同名の別変数 Mの引数のa f2の引数のa • 外のを隠すのでshadowingと呼ぶ • staticでなくても使えるけど外の ものと混ざらないよう要注意
  45. 45. null合体代入 • よくあるキャッシュ処理の類 • 複合代入 ??= で書けるように public string Data => _data = _data ?? GetData(); private string _data; private string GetData() { // 1度きりしか呼ばないけどものすごく重たい処理 } public string Data => _data ??= GetData(); private string _data; private string GetData() { // 1度きりしか呼ばないけどものすごく重たい処理 } 〇 int x = 1; int? y = null; var z = y ??= x; 16.1でちょっと変更ありそう • zはintかint?か • 16.0ではint? • 16.1ではint
  46. 46. .NET Core 3.0/.NET Standard 2.1推奨 非同期ストリーム: await/yield混在 • 生成側: 非同期イテレーター △ async Task<Page> GetPageAsync() { // ネット越しに取りたいので async await Task.Delay(1); // 代用 return new Page(); } IEnumerable<string> GetTitles(Page page) { foreach (var s in page.Sections) yield return s.Title; } 非同期メソッド(C# 5.0) イテレーター(C# 2.0) async IAsyncEnumerable<string> GetTitlesAsync() { Page page; do { page = await GetPageAsync(); foreach (var title in GetTitles(page)) yield return title; } while (page.HasNext); } • awaitとyieldの混在 • 戻り値はIAsyncEnumerable
  47. 47. 非同期ストリーム: await foreach/using • 消費側: 非同期foreach • await foreachの語順 • IAsyncEnumerable<T>に対して使える • (System.Collections.Generic名前空間) • 同期版同様、パターン ベース (同じメソッドさえ持っていれば任意の型に使える) △ .NET Core 3.0/.NET Standard 2.1推奨 await foreach (var title in GetTitlesAsync()) { Console.WriteLine(title); }
  48. 48. インデックス範囲構文 • 整数インデックスを持つ型の一定範囲(range)得るための構文 • x[^i] で Length - i 番目の要素参照 • Index構造体(System名前空間)が作られる • Index i = ^1; みたいな変数定義も可能 • x[i..j] で i(含む)からj(含まない)の範囲を参照 • Range構造体(System名前空間)が作られる • Range r = 1..^1; みたいな変数定義も可能 △ .NET Core 3.0/.NET Standard 2.1推奨
  49. 49. まとめ • C# 8.0はプレビューだけどもう使える • C# 8.0の新機能 • 割と今でも安定 : switch式、再帰パターン、using、??= • .NET Core 3.0依存 : 非同期ストリーム、Range • まだまだ変更だらけ : null許容参照型 • VS 16.1辺りで実装予定 : インターフェイスのデフォルト実装 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <LangVersion>preview</LangVersion> </PropertyGroup> </Project> (他にも細々)

×