Weitere ähnliche Inhalte Ähnlich wie C# 8.0 Preview in Visual Studio 2019 (16.0) (20) C# 8.0 Preview in Visual Studio 2019 (16.0)2. 今日の話
• C# 8.0の現状
• Visual Studio 2019 (16.0)はRTMだけど、C# 8.0はプレビュー
• プレビュー機能の有効化方法
• プレビューでも安定している機能としてない機能がある
• どれが安定していて、どれが安定していないか
• 主要なものを中心に新機能紹介
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. ちょっと延び気味?
• まあ、メジャー リリースなので
• 元々間隔広め
• C# 7.Xの時、しんどかったのかも?
• 7.2とか7.3辺り、バグが多かったから…
• 「プレビュー」という段階を経たい
• 機能盛りすぎかも
• スケジュール管理の仕方次第では「.NET Core 2.3」と「C# 7.4」が
あってもよかった疑惑
• といっても今更区切る余裕はない
• .NET Coreの方は切りどころが難しそう
• C#の方には切りどころあったと思う
• ただ、.NET Coreと足並み揃えたそう
6. C#プレビュー
• C# 7.2のとき
• C#チーム「プレビューとして出したい」
• Visual Studio「有効化しちゃった」
• C# 8.0では
• ちゃんと、プレビューを使うかどうか選択式に
• LangVersion (C#のバージョン指定オプション)の挙動も変更
• 元々あんなにハイペースでリリース
するつもりではなさそうだった
• 実際、結構バグが多かった
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側のオプション指定も必要
10. 言語バージョン (LangVersion)
• C#コンパイラー オプション
• csproj設定
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>
csc –langversion:preview
11. LangVersionに関する変更
• 今まで
• default : 最新のメジャー バージョン
• latest : 最新のマイナー バージョン
• 新ルール
• default : 最新のマイナー バージョン(latestと同じ)
• latest : 最新のマイナー バージョン(正式リリースのみ)
• latestMajor : 最新のメジャー バージョン
• preview : (プレビューを含めた)最新バージョン
• 既定動作(未指定時の挙動、default)がlatestと同じに
• latestMajor、previewを新設
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が有効化される
15. 安定度
• C# 8.0はプレビューという段階を経たい
• 「C# 7.4」があり得たかもしれないくらい、機能ごとに:
• 安定度にばらつきあり
• .NET Core 3.0への依存の有無あり
• C#チーム自身のドッグフーディング(Roslynへの適用)でも
「この機能なら現時点でも使っていい」区分あり※
※ 「Enable C# 8.0 in the code base」参照
16. • Visual Studio 16.0の時点で
凡例
〇
△
△
◎
×
… 割と安定。大きな変更はなさそう
… 割と不安定。使えるけど少し注意が必要
… 不安定。使えるけど、だいぶ人柱
… 安定。今後そんなに変更もなさそう
… 未実装。.NET Core 3.0正式リリースまでには実装されるはず
17. 割と安定してそうな機能
• 再帰パターン
• switch式
• usingの改善
• using変数宣言
• パターン ベースusing
• 静的ローカル関数
• ローカル関数での引数のシャドーイング
• その他こまごま
• null合体代入
• @$ (順序緩和)
この辺りは全部「Roslyn内
で使っていいもの」扱い
◎
〇 16.1で変更予定あり(演算子の優先度)
〇 16.1で変更予定あり(値型の時の挙動)
18. 不安定な機能
• .NET Core 3.0依存なものは多少怖い
(依存先がプレビューな以上、これから変更が掛かる可能性がある)
• 非同期ストリーム
• await/yield return混在
• await foreach/await using
• インデックス範囲構文(Index型/Range型)
• 単純にタスク山積みで不安定なやつ
• null許容参照型
使っていいけどpublicな
ところでは避けて
まだ使わない
△
△
まだ使わない
21. 大きなものだけまとめ
• 再帰パターン
• switch式
• usingの改善
• 非同期ストリーム
• インデックス範囲構文
• null許容参照型
• インターフェイスのデフォルト実装
◎
〇
◎
△
△
△
×
気持ち的には「C# 7.4」
くらいの感覚で使える
メジャー アップデート
にふさわしい大型機能
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. NRTは破壊的変更
• 既存コードの意味が変わる(破壊的変更)
• 単に参照型Tと書いたとき
• 今まで : nullがあり得た
• これから : nullを渡そうとすると警告
• なので、オプションを明示した時だけ有効に
• C# ソースコード中に#nullable
• csproj中にNullableContextOptions
△
#nullable enable
<PropertyGroup>
<NullableContextOptions>Enable</NullableContextOptions>
</PropertyGroup>
26. インターフェイスのデフォルト実装(DIM)
• default interface method、略してDIM
• インターフェイス内の関数メンバーに実装を持てるように
• インターフェイスに後からメンバーを足せるように
• Java/Swiftとの相互運用(Xamarin)でも必要
×
interface I
{
// 普通の(実装を持たない)メンバー
int M1();
// 実装を持つメンバー
public int M2() => 0;
}
インターフェイスを実装している
既存のクラス側に変更を求めない
27. DIMはランタイム側の変更が必要
• C#コンパイラーだけではできない機能
• TargetFramework によっては使えない
• 現状では.NET Core 3.0のみが対応
• WPFとかも.NET Core 3.0対応するので、必要なら移行を
• Xamarinはすぐに対応するはず
• Java/Swift相互運用のために必要
• 対応しているランタイムにはRuntimeFeatureクラス※に
DefaultImplementationsOfInterfacesプロパティがある
×
※ System.Runtime.CompilerServices名前空間
28. ランタイム側の変更は久しぶり
• .NET Framework 2.0 (2005年)以来の「ランタイム側の修正」
• .NET Coreが落ち着いたからできた
• やっと「移植で手一杯」のフェーズが終わった
• 他にも、Hardware Intrinsics※とか、ランタイム特殊対応が増えた
• いろいろなインストール形態ができるからこそ
• OS全体に影響を及ぼすようなインストール形態では怖くてできない
• .NET Coreなら、OS全体/アプリ単位を選べる
×
※ 特定CPU向けの専用命令を出力できるようにする仕組み
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. switch式
• switchを式で書けるように(後置き switch + =>)
var y = e switch
{
明治 => 1868,
大正 => 1912,
昭和 => 1926,
平成 => 1989,
令和 => 2019,
_ => throw new InvalidOperationException()
};
// y を使って何か
〇
33. switc式(注意点)
• 優先度が変わる
• a + x switch { ... } みたいなのの意味が変わる
• 今 : 比較演算子(< とか <= とか)と同じ辺り(かなり低め)
• 予定 : 単項演算子の直後(かなり高め)
• 型推論強化
• 代入先からの推論
or
• 共通基底推論
A x = y switch {… }
を A 扱い
AとBに共通基底Baseがあるとき
x switch { … => new A(), … => new B(), … }
を Base 扱い
〇
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. 再帰パターン
• 使えるパターンが増えた
• 対称性
◎
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. 再帰パターン
• 名前通り、再帰的に書ける
• 型が自明の時には () や {} だけで書ける
• () と {} の混在もできる
◎
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. 再帰パターン
• タプルとの組み合わせが結構有効
◎
static int CompareTo(int? x, int? y)
=> (x, y) switch
{
(null, null) => 0,
({}, null) => 1,
(null, {}) => -1,
({} x1, {} y1) => x1.CompareTo(y1),
};
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. 静的ローカル関数
• ローカル関数外の変数のキャプチャ
◎
static void M(int a)
{
// 外の変数・引数を使ってる(キャプチャしてる)
int f1(int x) => a * x;
// 使ってない
int f2(int x, int y) => x * y;
}
• キャプチャの有無で性能が変わる
• 大体、無い方が性能がよくなる
• 本当に意図的なキャプチャなのか
• 実は避けれたのにミスしてないか
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. .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. 非同期ストリーム: 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. インデックス範囲構文
• 整数インデックスを持つ型の一定範囲(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. まとめ
• 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>
(他にも細々)