一昨日、C# 8.0 に関するブログが出たわけですが。

個人的には「最近全然ブログ書かない C# チームが働いただと…」的な感想もあるんですが (C# 7.3 のときとか「半年前にリリースしてたわ」みたいなブログでした)。 近々プレビュー版が公開されるであろう C# 8.0 の予告記事です。

Visual Studio 15.9 正式リリースに続いて近々、Visual Studio 16.0 のプレビュー版も公開されて、 それと一緒に .NET Core 3.0 と C# 8.0 もプレビュー公開になると思われます。

.NET Framework 4.8 は未サポート?

で、「.NET Framework 4.8 は .NET Standard 2.1 に追従しないので、C# 8.0 に対応しない」みたいな感じのことが話題になっていますが。 これ、多少不正確でして。 正しくは、

という3つの機能に制限が掛かるだけ。他の機能は普通にどの TargetFramework でも動きます。 null 許容参照型とかパターン マッチングの完全版とか switch 式とかは .NET Framework 1.0 ですら動くと思われます。

必要ライブラリ

まあ、Range/Index構造体なんて結構小さい型なので、 例のごとく自分で同じ名前・同じ機能の構造体を書いてしまえば普通に古いランタイムでも C# 8.0 の機能を使えます。

といっても、Ranges の機能は、配列とか Span<T> が対応していて初めて役に立つものです。 (以下のような書き方をするためには、配列側が対応している必要あり。)

int[] data = { 1, 2, 3, 4, 5, 6 };
Span<int> span = data[1..^1];
foreach (var x in span)
    Console.WriteLine(x); // 2, 3, 4, 5

既存の型に Range 型対応を混ぜ込むのはちょっと無理なので、 その意味では .NET Standard 2.1 でないと大して役に立たない機能になります。

Async Streams の方も似たような感じ。 IAsyncEnumerable<T>インターフェイス自体の移植は簡単ですが、 それに対応したライブラリがないとあんまりおいしくないかもしれません。

インターフェイスのデフォルト実装

本当にどうあがいても .NET Framework 4.8 では動かせないのはこちら。 インターフェイスのデフォルト実装だけです。

ちなみに、どんな感じで「動かせない」かというと、

  • TargetFramework net48 でも、LangVersion 8.0 自体は選べる
    • 前述のとおり、大半の機能は普通に使えます
  • TargetFramework net48 を選んだ場合、デフォルト実装を使ったところだけコンパイル エラーになる

みたいな感じ。 デフォルト実装自体そんなに使う機能でもないと思うので、 大抵の状況では特に問題にならないと思います。

RuntimeFeature クラス

デフォルト実装の話、 要するに、LangVersion だけじゃなくて、TargetFramework によっても文法に分岐が掛かることになります。

ちなみに、正確にいうと、TargetFramework 自体を見て分岐しているのではなくて、 RuntimeFeatureクラスのプロパティがあるかないかで分岐しています。

以下のようなクラスなんですが、このDefaultImplementationsOfInterfacesプロパティが存在するランタイムでだけデフォルト実装が使えます。

public static partial class RuntimeFeature
{
#if FEATURE_DEFAULT_INTERFACES
        public const string DefaultImplementationsOfInterfaces = "DefaultImplementationsOfInterfaces";
#endif
    public const string PortablePdb = "PortablePdb";
    public static bool IsSupported(string feature) { throw null; }
}

.NET Standard 2.1

ちなみに、ちょっとわかりにくいですが、

  • 登場時期: .NET Core 3.0 = .NET Standard 2.1 = .NET Framework 4.8
  • 持っている機能: .NET Core 3.0 > .NET Standard 2.1 ≒ .NET Core 2.1 > .NET Framework 4.8 = .NET Standard 2.0

みたいな感じ。 .NET Standard 2.1 の主だった新機能はValueTask がらみと Span<T> がらみです。 .NET Core 2.1 ですでにおなじみ(?)のやつ。

(Ranges と Async Streams、最初は .NET Core 3.0 でないと使えないのでは疑惑も多少。 .NET Standard 2.1 に入るのかな…)

.NET Framework の今後の扱い

ということで、C# 8.0 への対応という意味では .NET Framework でもそこまで大した問題にはならないと思います。

が、まあ、「.NET Framework 4.8 は最新のものに追従しない」というのは事実。

とりあえず、

  • 同世代の .NET Core 3.0 では Windows 限定機能(WPF, UWP)にもついに対応する
    • もう .NET Framework の方でしか使えない機能が残っていないはず
    • 新規案件で .NET Framework を使うメリットがもう何もない
  • 保守モードな既存のアプリにまで .NET Core への移行を要請しないだけ良心的
    • そのための「保守アップデート」が .NET Framework 4.8
    • セキュリティ パッチとかはきっちり当てて出す

という感じ。 この辺りに関しては、 C# 8.0 のブログ以前に、 今月初旬に出てる以下のブログの方ですでに告知済みだったり。

こっちを見る限り、.NET Framework 4.8 が追従しないのは、デフォルト実装だけじゃなくて、 Span<T>がらみの方が主みたいです。

Span<T>構造体」で書いているように、 Span<T>にはslow版(古いランタイムでも動く実装)とfast版(.NET Core 2.1 以降でないと動かない安全かつ高速な実装)の2種類あります。 そのSpan<T>を使ったライブラリの中には、fast版の安全性保証がないとまずいものもいくつかあって、 そういうものは .NET Standard 2.0 以前向けには提供されていません。

.NET Framework 4.8はこのfast Span<T>対応もできておらず、 なので、.NET Standard 2.0 のまま据え置きということになります。