C# の歴史

この記事では、C# 言語の各メジャー リリースの履歴について説明します。 C# チームは、引き続き新機能を刷新および追加していきます。 今後のリリースに向けて検討される機能を含め、言語機能ステータスについての詳細は GitHub の dotnet/roslyn リポジトリで見つけられます。

重要

C# 言語の一部の機能は、C# の仕様で定義されている "標準ライブラリ" の型とメソッドに依存しています。 .NET プラットフォームでは、さまざまなパッケージでそれらの型とメソッドが提供されています。 一例として、例外処理があります。 すべての throw ステートメントまたは式は、スローされたオブジェクトが Exception から派生していることを確認するために、チェックされます。 同様に、すべての catch は、キャッチされた型が Exception から派生していることを確認するために、チェックされます。 バージョンごとに新しい要件が追加されている場合があります。 古い環境で言語の最新機能を使用するには、特定のライブラリをインストールする必要がある場合があります。 これらの依存関係については、特定のバージョンごとに用意されたページに記載されています。 この依存関係の経緯と詳細については、言語とライブラリ間の関係に関する記事をご覧ください。

C# バージョン 1.0

振り返ってみると、Visual Studio .NET 2002 でリリースされた C# バージョン 1.0 は Java に似ていました。 ECMA で掲げられた設計目標の一環として、C# は "シンプルかつモダンな汎用オブジェクト指向言語" を目指していました。当時、Java に似ていることは、初期の設計目標を達成したことを意味していました。

しかし、今 C# 1.0 を振り返ってみると、少し混乱するかもしれません。 現在では当たり前となっている組み込みの非同期機能や、ジェネリック関連の優れた機能の一部は備わっていませんでした。 実際、ジェネリック全体がなかったのです。 そして LINQ も、 まだ使用できませんでした。 このような追加機能が登場するまでにはまだ数年かかります。

C# バージョン 1.0 は、現在のバージョンと比べると、機能がはぎ取られたように見えます。 気がつくと冗長なコードを記述している場合があるでしょう。 しかしそれでも、千里の道も一歩からです。 C# バージョン 1.0 は、Windows プラットフォームにおける Java の実行可能な代替手段でした。

C# 1.0 の主な機能:

C# バージョン 1.2

Visual Studio .NET 2003 に付属の C# バージョン 1.2。 言語に対する細かな機能強化がいくつか含まれています。 最も重要な点は、このバージョン以降、IEnumerator によって IDisposable が実装された場合、foreach ループ内で生成されたコードでは、その IEnumerator 上で Dispose が呼び出されているということです。

C# バージョン 2.0

ここから面白くなり始めます。 Visual Studio 2005 と共に 2005 年にリリースされた C# 2.0 の主な機能をいくつか見てみましょう。

その他の C# 2.0 機能では既存の機能に新たな能力を追加しました。

  • ゲッター/セッター別のアクセシビリティ
  • メソッド グループの変換 (デリゲート)
  • 静的クラス
  • デリゲート推論

C# は汎用的なオブジェクト指向 (OO) 言語として始まったかもしれませんが、C# バージョン 2.0 ではそれが急速に変化しました。 マイクロソフトが本腰を入れると、いくつかの開発者の重大な問題点を追究しました。 そしてその問題点を著しく追究しました。

ジェネリックでは、型とメソッドがタイプ セーフを維持しながら任意の型に対して操作を行えます。 たとえば、List<T> があると、List<string> または List<int> を使用し、これらの文字列または整数に対してタイプ セーフ操作を実行しながら、これらを反復処理することができます。 ジェネリックを使用する方が、ArrayList から派生する ListInt 型を作成したり、すべての操作で Object からキャストしたりするよりも優れています。

C# バージョン 2.0 で、反復子が導入されました。 手短に言うと、反復子を使用すると、List 内のすべての項目 (またはその他の列挙可能な型) を foreach ループで確認することができます。 反復子を言語のファーストクラスの部分として持つことで、言語の読みやすさと、ユーザーのコードについて推論する能力が劇的に高まりました。

それでもまだ、C# は Java にほんの少し後れを取り続けていました。 Java は、ジェネリックと反復子が含まれたバージョンを既にリリースしていました。 しかし間もなく、2 つの言語は別々の進化を続けていくことになります。

C# バージョン 3.0

C# バージョン 3.0 は、Visual Studio 2008 と共に 2007 年後半に登場しましたが、すべての言語機能が搭載されたのは、実際には .NET Framework バージョン 3.5 からでした。 このバージョンは、C# の成長において大きな変化を遂げました。 このバージョンで、C# は真に強力なプログラミング言語としての地位を確立しました。 このバージョンでの主な機能をいくつか見てみましょう。

今になって考えると、これらの機能の多くは必然で切り離せないものに思えます。 これらすべてが戦略的に組み合わさっています。 C# のこのバージョンでの目玉機能はクエリ式 (別名、統合言語クエリ、LINQ) だったと考えられています。

もう少し異なる見解では、式ツリー、ラムダ式、匿名型が、LINQ が構築される基になっていると分析しています。 しかし、いずれにしても、C# 3.0 は革新的な概念を提示しました。 C# 3.0 から、C# をオブジェクト指向と関数型言語のハイブリッドに変換するための下準備が始まりました。

いろいろありますが、特に、今では記述できるようになった SQL スタイル、コレクションに対して操作を実行する宣言型クエリなどがあります。 for ループを記述して整数のリストの平均を計算する代わりに、今では単純に list.Average() でこれを行うことができます。 クエリ式と拡張メソッドの組み合わせにより、整数のリストが非常にスマートになったように見えるようになりました。

ユーザーが概念を真に理解して統合するには時間がかかりましたが、徐々にそうなりました。 そして数年後の現在、コードはもっと簡潔、シンプルかつ機能的になりました。

C# バージョン 4.0

Visual Studio 2010 でリリースされた C# バージョン 4.0 は、バージョン 3.0 の革新的なステータスに応えるための困難な時期だったと言えるでしょう。 バージョン 3.0 で C# は Java の影から脱却して、主要な言語となったのです。 この言語は急速に洗練されました。

次のバージョンでは、いくつかの興味深い新機能が導入されました。

埋め込まれた相互運用機能型により、アプリケーション用に COM 相互運用アセンブリを作成する展開の苦労が軽減されました。 ジェネリックの共変性と反変性は、ジェネリックを使用する権限を強化しますが、少々アカデミックで、最も高く評価されているのは、おそらくフレームワークとライブラリの作成者からでしょう。 名前付きパラメーターと省略可能なパラメーターは、多くのメソッドのオーバーロードを排除して、利便性を高めることができます。 しかし、これらの機能はいずれもパラダイムを変えるほどのものではありませんでした。

主要な機能は、dynamic キーワードの導入でした。 C# バージョン 4.0 で導入された dynamic キーワードにより、コンパイル時の型指定でコンパイラをオーバーライドできるようになりました。 dynamic キーワードを使用することで、JavaScript のような動的に型指定された言語と同様のコンストラクトを作成することができます。 dynamic x = "a string" を作成してから、それに 6 を追加して、実行時までそのままにして次に発生する必要のあることを整理することができます。

動的バインドには潜在的なエラーの可能性がありますが、同時に、この言語が持つ大きな力にもなっています。

C# バージョン 5.0

Visual Studio 2012 でリリースされた C# バージョン 5.0 は、この言語の専心的なバージョンでした。 このバージョンに対するほぼすべての努力が、非同期プログラミングの async および await モデルというもう一つの革新的な言語の概念に注がれました。 主要な機能の一覧を次に示します。

関連項目

呼び出し元情報属性を使用すると、さまざまな定型リフレクション コードを使用しなくても、実行しているコンテキストに関する情報を簡単に取得できます。 診断とログ記録のタスクでは、さまざまな用途があります。

しかしこのリリースの真の主役は、asyncawait です。 2012 年にこれらの機能が登場したとき、C# はファーストクラスの一部として非同期を言語に採用したことで再び流れを変えました。 これまでに、長時間実行される操作とコールバックの Web の実装に対処したことがあれば、おそらくこの言語機能を気に入っているでしょう。

C# バージョン 6.0

C# バージョン 3.0 と 5.0 では、主要な新機能がオブジェクト指向言語に追加されました。 Visual Studio 2015 と共にリリースされたバージョン 6.0 では、主要な目玉機能を投入する代わりに、C# プログラミングをより生産的にする多くの小さな機能をリリースしました。 その一部を次に示します。

その他に次の新機能があります。

  • インデックス初期化子
  • Catch/Finally ブロックでの Await
  • ゲッターのみのプロパティの既定値

これらの機能は、単独でも興味深い機能ですが、 全体として見てみると、興味深いパターンが見えます。 このバージョンで、コードをより簡潔で読みやすくするため、C# から定型表現が排除されました。 そのため、クリーンで単純なコードが好きな人に、この言語バージョンは大当たりしました。

マイクロソフトはこのバージョンとともにもう 1 つ別のことを行いましたが、それ自体が従来の言語機能ではありませんでした。 サービスとしてのコンパイラ Roslyn をリリースしたのです。 C# コンパイラは現在、C# で記述され、プログラミングのための取り組みの一環として、コンパイラを使用することができます。

C# バージョン 7.0

C# バージョン 7.0 は、Visual Studio 2017 でリリースされました。 このバージョンには、C# 6.0 から続くいくつかの革新的で優れた機能がありますが、サービスとしてのコンパイラはありません。 新機能の一部を次に示します。

その他の機能:

これらすべての機能が素晴らしい新機能を開発者に提供し、これまでよりもさらにクリーンなコードを記述する機会を提供します。 ハイライトは、out キーワードで使用するために変数の宣言を凝縮することと、タプルを通じて複数の戻り値を許可することです。

しかし C# はさらに広範に使用されています。 .NET Core は現在、任意のオペレーティング システムを対象としており、クラウドと移植性をしっかりと見据えています。 この言語の設計者たちは、新機能を考え出すことに加え、こうした新たな能力についても多くの思考と時間を費やしています。

C# バージョン 7.1

C# は、C#7.1 で "ポイント リリース" のリリースを開始しました。 このバージョンでは、言語バージョン選択構成要素、3 つの新しい言語機能、新しいコンパイラ動作が追加されます。

このリリースの新しい言語機能は次のとおりです。

  • asyncMainメソッド
    • アプリケーションのエントリ ポイントに async 修飾子を設定できます。
  • default リテラル式
    • ターゲットの種類を推論できるとき、既定の値式で既定のリテラル式を使用できます。
  • 推論されたタプル要素の名前
    • タプル要素の名前は、多くの場合、タプル初期化から推論できます。
  • ジェネリック型パラメーターのパターン マッチ
    • 型がジェネリック型パラメーターである変数にパターン マッチ式を使用できます。

最後に、コンパイラには、参照アセンブリ生成を制御する 2 つのオプション、-refout-refonly があります。

C# バージョン 7.2

C# 7.2 では、いくつかの小規模な言語機能が追加されました。

  • stackalloc 配列の初期化子。
  • パターンをサポートするすべての型と共に fixed ステートメントを使用できます。
  • ピン留めを使用せずに固定フィールドにアクセスできます。
  • ref ローカル変数を再割り当てすることができます。
  • 構造体が不変であり、そのメンバー メソッドに in パラメーターとして渡す必要がある場合は、これを示す readonly struct 型を宣言します。
  • パラメーターの in 修飾子を追加し、引数が参照によって渡されるが、呼び出されたメソッドでは変更されないことを指定します。
  • メソッド戻りの ref readonly 修飾子を使用し、メソッドが参照によってその値を戻しますが、そのオブジェクトに対する書き込みを許可しないことを指定します。
  • 構造体型がマネージド メモリに直接アクセスし、常にスタック割り当てが必要であることを示すには、ref struct 型を宣言します。
  • 追加のジェネリック制約を使用できます。
  • 末尾以外の名前付き引数
    • 名前付き引数の後ろに位置引数を続けることができます。
  • 数値リテラルでの先頭のアンダースコア (_)
    • 数値リテラルの印刷桁の前に先頭のアンダースコア(_) を含めることができるようになりました。
  • private protected アクセス修飾子
    • private protected アクセス修飾子によって、同じアセンブリ内の派生クラスのアクセスが有効になります。
  • 条件付きの ref
    • 条件式 (?:) の結果を参照にすることができるようになりました。

C# バージョン 7.3

C# 7.3 リリースには 2 つの主要なテーマがあります。 1 つ目のテーマは、アンセーフ コードと同様のパフォーマンスをセーフ コードで確保するための機能の提供です。 2 つ目のテーマは、既存の機能のインクリメンタルな改善の提供です。 新しいコンパイラ オプションもこのリリースで追加されました。

次の新機能は、セーフ コードのパフォーマンス向上のテーマをサポートします。

  • ピン留めを使用せずに fixed フィールドにアクセスできます。
  • ref ローカル変数を再割り当てできます。
  • stackalloc 配列で初期化子を使用できます。
  • パターンをサポートする型と共に fixed ステートメントを使用できます。
  • より汎用的な制約を使用できます。

既存の機能が次のように強化されました。

  • タプル型を使用して ==!= をテストできます。
  • 式の変数をより多くの場所で使用できます。
  • 自動実装プロパティのバッキング フィールドに属性をアタッチできます。
  • 引数が in によって異なる場合のメソッド解決が改善されました。
  • オーバーロードの解決のあいまいなケースが削減されました。

新しいコンパイラ オプションは次のとおりです。

  • -publicsign: オープン ソース ソフトウェア (OSS) のアセンブリの署名を可能にします。
  • -pathmap: ソース ディレクトリのマッピングを提供します。

C# バージョン 8.0

C# 8.0 は、特に C# .NET Core をターゲットとする最初のメジャー リリースです。 新しい CLR 機能に依存する機能と、.NET Core にのみ追加されたライブラリの型に依存する機能があります。 C# 8.0 では、C# 言語に次の機能と機能強化が追加されています。

既定のインターフェイス メンバーには、CLR の拡張機能が必要です。 これらの機能は、CLR for .NET Core 3.0 で追加されました。 範囲とインデックス、および非同期ストリームには、.NET Core 3.0 ライブラリの新しい型が必要です。 null 許容参照型は、コンパイラに実装されていますが、引数と戻り値の null 状態に関するセマンティック情報を提供する注釈がライブラリに付けられている場合に非常に役立ちます。 このような注釈は .NET Core ライブラリに追加されています。

C# バージョン 9

C# 9 は .NET 5 でリリースされました。 これは、.NET 5 リリースを対象とするすべてのアセンブリ用の既定の言語バージョンです。 それには、次の新機能と強化された機能が含まれます。

C# 9 には、以前のリリースから引き続き 3 つのテーマ (手続きの削除、アルゴリズムからのデータの分離、より多くの場所でのより多くのパターンの提供) があります。

上位レベルのステートメントは、メイン プログラムが読みやすくなることを意味します。 必要な手続きが少なくなります。名前空間、Program クラス、static void Main() はすべて必要ありません。

records の導入により、等値の値セマンティクスに従う参照型に簡潔な構文が提供されます。 一般に最小限の動作を定義するデータ コンテナーを定義するには、これらの型を使用します。 init 専用セッターでは、レコードで非破壊的変更 (with 式) を行うための機能が提供されます。 C# 9 では、派生レコードで仮想メソッドをオーバーライドし、基本メソッドの戻り値の型から派生した型を返すことができるように、共変の戻り値の型も追加されます。

パターン マッチング機能は、いくつかの方法で拡張されています。 数値型で "範囲パターン" がサポートされるようになりました。 andornot パターンを使用して、パターンを結合できます。 かっこを追加して、複雑なパターンを明確にできます。

別の機能セットでは、C# でのハイ パフォーマンス コンピューティングがサポートされています。

  • nint および nuint 型により、ターゲット CPU でのネイティブ サイズの整数型がモデル化されます。
  • 関数ポインターでは、デリゲート オブジェクトを作成するために必要な割り当てを回避しながら、デリゲートに似た機能が提供されます。
  • localsinit 命令を省略して、命令を削減できます。

もう 1 つの一連の機能強化では、"コード ジェネレーター" によって機能を追加するシナリオがサポートされています。

  • モジュール初期化子は、アセンブリの読み込み時にランタイムによって呼び出されるメソッドです。
  • 部分メソッドでは、新しいアクセス可能な修飾子と非 void の戻り値の型がサポートされます。 このような場合は、実装を提供する必要があります。

C# 9 では、コードの記述と読み取りの両方において、開発者の生産性を向上させる他の小さな機能が多数追加されています。

  • ターゲット型による new
  • static 匿名関数
  • ターゲット型による条件式
  • foreach ループの拡張機能 GetEnumerator() サポート
  • ラムダ式で破棄パラメーターを宣言できます
  • ローカル関数に属性を適用できます

C# 9 のリリースでは、C# を最新の汎用プログラミング言語に保つ作業が続けられています。 最新のワークロードとアプリケーションの種類が引き続き機能によってサポートされます。

C# バージョン 10

C# 10 によって、C# 言語に次の機能と機能強化が追加されています。

"プレビュー" モードでは、追加の機能を使用できます。 これらの機能を試していただき、それに関するフィードバックを是非お送りください。 これらは、最終的なリリースの前に変更される可能性があります。 これらの機能を使用するには、プロジェクトで <LangVersion>Preview に設定する必要があります。

C# 10 では、手続きの削除、アルゴリズムからのデータの分離、.NET ランタイムのパフォーマンスの向上をテーマに引き続き取り組んでいます。

機能が多くなることで、同じ概念を表現するために入力するコードが少なくなります。 "レコード構造体" では、"レコード クラス" が実行するものと同じメソッドの多くが合成されます。 構造体と匿名型では "with 式" がサポートされます。 "グローバルな using ディレクティブ" と "ファイル スコープ名前空間宣言" によって、依存関係と名前空間の編成が明確に表現されます。"ラムダの機能強化" により、ラムダ式を使用する場所で簡単に宣言できるようになります。 新しいプロパティ パターンと分解の機能強化により、さらに簡潔なコードが作成されます。

新しい補間された文字列ハンドラーと AsyncMethodBuilder の動作により、パフォーマンスを向上することができます。 これらの言語機能は .NET 6 のパフォーマンス向上を実現するために、.NET ランタイムで利用されていたものです。

また C# 10 では、.NET のリリースに合わせた年次サイクルへのさらなる転換がなされています。 すべての機能を 1 年間の時間枠で完備することはできないため、C# 10 の 2 つの "プレビュー" 機能を試すことができます。 "汎用属性" と "インターフェイスの静的抽象メンバー" の両方を使用できますが、これらはプレビュー機能であり、最終リリース前に変更される可能性があります。

記事は、NDepend ブログで元々公開されていたものです (提供: Erik Dietrich および Patrick Smacchia)。