この項目では、プログラミング言語について説明しています。音名のC♯については「嬰ハ 」をご覧ください。
本来の表記は「
C# 」です。この記事に付けられたページ名は
技術的な制限 または
記事名の制約 により不正確なものとなっています。
C# (シーシャープ)は、マイクロソフト が.NET 向けに開発した汎用プログラミング言語 であり[ 1] 、ECMA およびISO で標準化 されている。C系言語やJava に影響を受けた構文を持ち、Windows を含む複数のプラットフォームで利用され、デスクトップ 、Web 、クラウド 、モバイル、ゲーム開発 などに用いられる。
開発にはボーランドのTurbo Pascal やDelphi を開発したアンダース・ヘルスバーグ を筆頭として多数のDelphi開発陣が参加している。構文はC系言語(C言語 、C++ 、Java など)の影響を受けており、その他の要素には、以前、ヘルスバーグが所属していたボーランド 設計のDelphi の影響が見受けられる。また、主要言語へのasync/await 構文や、ヘルスバーグが言語設計に関わるTypeScript でのジェネリクス 採用など、他言語への影響も見られる。
C#は共通言語ランタイム (CLR)などの共通言語基盤 (CLI)が解釈する共通中間言語 (CIL)にコンパイル されて実行される。
また、C#はマルチパラダイムをサポートする汎用高レベルプログラミング言語で、静的型付け 、タイプセーフ 、スコープ 、命令型 、宣言型 、関数型 、汎用型 、オブジェクト指向 (クラスベース )、コンポーネント指向 のプログラミング分野を含んでいる。他にも自動ボックス化 、デリゲート 、プロパティ 、インデクサ 、カスタム属性 、ポインタ 演算操作、構造体 (値型オブジェクト)、多次元配列 、可変長引数 、async/await構文 、null 安全などの機能を持つ。また、Javaと同様に大規模ライブラリ 、プロセッサ・アーキテクチャ に依存しない実行形態、ガベージコレクション 、JITコンパイル による実行の高速化、AOTコンパイラ による高速実行、などが実現されている(もっともこれらはC#の機能というより.NET によるものである)。
共通言語基盤といった周辺技術も含め、マイクロソフトのフレームワークである「.NET」 の一部である。また、以前のVisual J++ で「非互換なJava」をJavaに持ち込もうとしたマイクロソフトとは異なり、その多くの[ 注釈 1] 仕様を積極的に公開し、標準化機構に託して自由な利用を許す[ 注釈 2] など、同社の姿勢の変化があらわれている。
.NET構想における中心的な開発言語であり、XML Webサービス やASP.NET の記述にも使用される。他の.NET系の言語でも記述可能だが、.NET APIはC#からの利用を第一に想定されており、他の.NET系言語(特に2023年以降新構文の追加なしと宣言されたVB.NET[ a 2] )では利用できない、あるいは将来的に利用できなくなる機能が存在する。
マイクロソフトの統合開発環境 (Microsoft Visual Studio )では、Microsoft Visual C# がC#に対応している。また、Visual Studio Code に専用のC#向け拡張(C# DevKit)を導入することでクロスプラットフォームで開発することが可能[ a 3] 。
共通言語仕様のCLSによって、他のCLS準拠の言語(F# やVisual Basic .NET 、C++/CLI など)と相互に連携することができる。
さまざまな意味において [要追加記述 ] 、基盤である共通言語基盤(CLI)の多くの機能を反映している言語であるといえる。C#にある組み込み型のほとんどは、共通言語基盤のフレームワークに実装されている値型と対応している。
しかし、C#の言語仕様はコンパイラのコード生成については何も言及していないため、CLR に対応しなければならないとか、CIL などの特定のフォーマットのコードを生成しなければならないとかいうことは述べられていない。
そのため、理論的にはC++やFORTRAN のように環境依存のマシン語を生成することも可能である。しかし、現在存在するすべてのC#コンパイラはCLIをターゲットにしている。
.NET 7.0以降で可能になった事前コンパイルの一種である「Native AOT」 でデプロイすることで実行可能な環境依存のバイナリを出力することが可能である。しかしながらこの手法もCLIとランタイムを事前に各アーキテクチャ向けのバイナリに変換しているだけであり、CLIを経由することに変わりはない。[ a 28]
特殊な例としては、UnityのScripting Backendである「IL2CPP 」[ 9] や「Burst」[ 10] がある。IL2CPPはC#をコンパイルしたCILをさらにC++コードへと変換後、ネイティブバイナリへC++コンパイラによってコンパイルされる。BurstはC#をコンパイルしたCILをLLVMコンパイラ によってネイティブバイナリへコンパイルするものである。
最新のC#ではHello World を下記の通りに1行で記述できる[ a 29] 。
Console . WriteLine ( "Hello World!" ); 上記のコードは、コンパイラによって下記の様なコードに展開される。
using System ; namespace Wikipedia { class Program { static void Main ( string [] args ) { Console . WriteLine ( "Hello World!" ); } } } 次の言語機能によって実現されている。ただし、未対応の古いコンパイラを用いる場合は、省略できない。
C#では、CやC++と比較してさまざまな制限や改良が加えられている。また、仕様の多くはC#言語というよりは、基盤である.NETそのものに依拠している。Javaで導入された制限および改良をC#でも同様に採用しているものが多いが、C#で新たに導入された改良がのちにJavaにも同様に採用されたものもある。その例を次に挙げる。
外のブロックで宣言した変数と同じ名前の変数を、内のブロックで再宣言(シャドウ)してはいけない。再宣言は便利なこともあれば、混乱や曖昧のもとと主張されることもあるが、C#では禁止されている。 C#にはブール型 boolが存在し、while 文やif 文のように条件をとるステートメントには、bool型の式を与えなければならない。C言語では、ブール型が無くint型(0を偽とし、非0を真とする)に兼用させた上、(ヌルポインタ を偽とみなすこととするといろいろと便利だった、ということもあり)ポインタでもwhile文やif文に与える式にできる、という仕様としていた。これは便利なこともあったが、本来比較式を記述すべきところで誤って代入式を記述してもコンパイル適合となってしまうなど、ミスが見逃されることもあった。C#ではミスを防止するため [要出典 ] に、そのような仕様ではなくブール型を独立させ、またブール型を厳密に要求する場所を多くしている。 switch 文に整数型あるいは整数型に準ずる型のみならず、文字列型stringを使用できる。caseラベルには、整数型あるいは整数型に準ずる型の定数のみならず、文字列リテラル(文字列定数)を使用できる。組み込み型のサイズおよび内部表現が仕様で定められており、プラットフォームや処理系に依存しない。浮動小数点数 はIEEE 754 に準拠する[ a 32] 。文字および文字列はUTF-16 エンコーディングを採用する[ a 33] 。C# 9.0以降では CPUによってサイズの異なる整数型nint/nuintが追加された[ b 11] C# 11.0 以降では文字列をUTF-8 として扱う「UTF-8文字列リテラル」が追加された[ a 34] ポインタをサポートする。ポインタはunsafeスコープ内のみで使用することができ、適切な権限をもつプログラムのみがunsafeとマークされたコードを実行することができる。オブジェクトへのアクセスの大部分は管理された安全な参照によってなされ、大部分の算術演算はオーバフローのチェックがなされる。unsafeポインタは値型や文字列を指すことができる。セーフコードでは、必ずしもそうする必要はないものの、IntPtr型を通してポインタをやりとりすることができる。 マネージドなメモリを明示的に解放する方法は存在せず、参照されなくなったメモリはガベージコレクタ によって自動的に解放される。ガベージコレクタは、メモリの解放忘れによって起こるメモリリークを解消する。C#は、データベース接続のようなアンマネージドなリソースに対しても明示的に制御する方法を提供している。これはIDisposableインタフェース とusingステートメントまたはusing宣言によってなされる。 例えばC/C++のprintf ()関数のように名前空間 レベルに存在するフリー関数を定義することはできない。ほとんどの場合クラスおよび構造体は名前の衝突を避けるために名前空間に所属する。 名前空間は階層構造をもつ。つまり、名前空間は他の名前空間の中に宣言することができる。 組み込みの値型を含めたすべての型は、objectクラス (System.Object) の派生型である。つまりobjectクラスのもつすべてのプロパティやメソッドを継承する。例えば、すべての型はToString()メソッドをもつ。 クラス (class) は参照型であり、構造体 (struct) および列挙型 (enum) は値型である。構造体はクラスよりも軽量で、C/C++との相互運用性に優れるが、派生型を定義することができない。 クラスおよび構造体は複数のインタフェースを実装することができるが、多重継承 はサポートされない。 C#はC++に比べて型安全である。既定の暗黙変換は、整数の範囲を広げる変換や、派生クラスから基底クラスへの変換といった、安全な変換のみに限定される。これは、コンパイル時、JITコンパイル 時、そして一部の動的なケースでは実行時に強制される。ブール型と整数型、列挙型と整数型、の間は暗黙変換はできない。暗黙変換をユーザー定義する際は、明示的にそのように指定しなければならない。これはC++のコンストラクタとは違った仕様である。 C#は「Null安全」である。Null許容型 、Null許容参照型を持ち、Null合体演算子 などの構文・演算子を持つ。 列挙型のメンバーは、列挙型のスコープの中に置かれる。また、列挙型の定数名を取得することができる。さらに、列挙型の定数名から動的に定数値を得ることができる。 アクセサ の定義と利用を簡略化するためにプロパティ 構文を利用できる。C++およびJavaにおけるカプセル化 では、通例getter/setterアクセサとなるメンバー関数あるいはメソッドを定義して利用するが、C#ではプロパティ機能により、カプセル化を維持しつつ、あたかもフィールド を直接読み書きするような直感的な構文でオブジェクトの状態にアクセスすることができる。プロパティによってメンバーのアクセス制御やデータの正当性チェックを実行することができる。なお、イベントハンドラー に利用するデリゲートのカプセル化にはイベント構文 (event) が用意されている。ジェネリクス (総称型 )の採用(C# 2.0 以降)。C++のテンプレート、Javaのジェネリックスと異なりコンパイル後も型情報が保持される。また、Javaのジェネリクスと異なりプリミティブ型も型変数として使うことができる。部分型(英 :Partial Type )が導入された[ a 5] 。以下のようにクラスや構造体の宣言にpartial修飾子をつけることで、その宣言を分割することができる。
partial class MyClass { int a ; } partial class MyClass { int b ; } これは以下と同義である。
これによって、巨大なクラスを分割したり、自動生成されたコードを分離したりすることができる。partial修飾子はすべての宣言につける必要がある[ 11] 。
ジェネリクス が導入された[ a 5] 。これは.NET Framework 2.0の機能である。クラス、構造体、インタフェース、デリゲート、メソッドに対して適用することができる。
.NETのGenericsはC++のテンプレート 、あるいはJavaにおけるそれとも異なるもので、コンパイルによってではなく実行時にランタイムによって特殊化される。これによって異なる言語間の運用を可能にし、リフレクションによって型パラメーターに関する情報を取得することができる。また、where節によって型パラメーターに制約を与えることができる。一方、C++のように型パラメーターとして式 を指定することはできない。なお、ジェネリックメソッドの呼び出し時に引数によって型パラメーターが推論できる場合、型パラメーターの指定は省略できる[ 12] 。
静的クラスが導入された[ a 5] 。static属性をクラスの宣言につけることで、クラスはインスタンス 化できなくなり、静的なメンバーしか持つことができなくなる[ 11] 。
yieldキーワードによるコルーチン を使うことで、イテレータの生成 を楽に実装できるようになった。
クロージャ の機能を提供する匿名デリゲート が導入された。
プロパティのgetもしくはsetアクセサのどちらかにアクセス修飾子を指定することでアクセス制御が別個にできるようになった[ a 5] 。次の例では、getアクセサはpublic、setアクセサはprivateである[ 13] 。
public class MyClass { private string status = string . Empty ; public string Status { get { return status ; } private set { status = value ; } } } nullを保持できる値型、Nullableが導入された[ a 5] 。
int? i = 512 ; i = null ; int? j = i + 500 ; //jはnullとなる。nullとの演算の結果はnullになる。 int?はNullable<int>の糖衣構文 である。また、nullを保持しているNull許容型のインスタンスをボックス化 しようとすると、単に空参照(null)に変換される[ a 35] [ 14] 。
int? x = null ; object o = x ; System . Console . WriteLine ( o == null ); //Trueが出力される また、null結合演算子 (??)が導入された。これは、nullでない最初の値を返す。
object obj1 = null ; object obj2 = new object (); object obj3 = new object (); return obj1 ?? obj2 ?? obj3 ; // obj2 を返す この演算子は主にNullable型を非Nullable型に代入するときに使われる。
int? i = null ; int j = i ?? - 1 ; // nullをint型に代入することはできない var キーワードが導入され、型推論 を利用したローカル変数の宣言ができるようになった[ a 5] 。
var s = "foo" ; // 上の文は右辺が string 型であるため、次のように解釈される: string s = "foo" ; // 以下に挙げる文は誤りである(コンパイルエラーとなる): var v ; // 初期化式を欠いている (型を推論する対象が存在しない) var v = null ; // 型が推論できない (曖昧である) 拡張メソッド(英 :extension method )が導入された[ a 5] 。既存のクラスを継承して新たなクラスを定義することなく、新たなインスタンスメソッドを疑似的に追加定義することができる。具体的には、入れ子になっていない、非ジェネリックの静的クラス内に、this 修飾子をつけた、拡張メソッドを追加する対象の型の引数を最初に持つメソッドをまず定義する。これによって、通常の静的メソッドとしての呼び出しの他に、指定した型のインスタンスメソッドとしての呼び出しを行うことができるメソッドを作ることができる。以下に例を挙げる:
public static class StringUtil { public static string Repeat ( this string str , int count ) { var array = new string [ count ]; for ( var i = 0 ; i < count ; ++ i ) array [ i ] = str ; return string . Concat ( array ); } } この例は、文字列(string型のインスタンス)を指定した回数繰り返し連結したものを返すメソッドRepeatを、既存のstring型に追加している。このメソッドは、以下のように呼び出すことができる:
// 静的メソッドとしての呼び出し StringUtil . Repeat ( "foo" , 4 ); // 拡張メソッドとしての呼び出し "foo" . Repeat ( 4 ); // (どちらの例も "foofoofoofoo" を返す) また、列挙型 やインタフェースなど本来メソッドの実装を持ち得ない型に、見かけ上インスタンスメソッドを追加することも可能である。以下に例を挙げる:
public enum Way { None , Left , Right , Up , Down } public static class EnumUtil { public static Way Reverse ( this Way src ) { switch ( src ) { case Way . Left : return Way . Right ; case Way . Right : return Way . Left ; case Way . Up : return Way . Down ; case Way . Down : return Way . Up ; default : return Way . None ; } } } このメソッドは以下のように呼び出すことができる:
Way l = Way . Left ; Way r = l . Reverse (); // Way.Right 拡張メソッドは糖衣構文 の一種であり、カプセル化 の原則に違反するものではないが、必要な場合に限り注意して実装することがガイドラインとして推奨されている[ a 36] 。
部分メソッドが導入された[ a 5] 。部分型(partial型)内で定義されたprivateで、かつ戻り値がvoidのメソッドにpartial修飾子をつけることでメソッドの宣言と定義を分離させることができる。定義されていない部分メソッドは何も行わず、何らエラーを発生させることもない。例えば:
partial class Class { partial void DebugOutput ( string message ); void Method () { DebugOutput ( "Some message" ); Console . WriteLine ( "Did something." ); } } 上のコードにおいてMethod() を呼び出すと、Did something. と表示されるだけだが、ここで以下のコード:
partial class Class { partial void DebugOutput ( string message ) { Console . Write ( "[DEBUG: {0}] " , message ); } } を追加した上でMethod()を呼び出すと、[DEBUG: Some message] Did something. と表示される。
ラムダ式が導入された[ a 5] 。この名前はラムダ計算 に由来する。
以下の匿名メソッド
// iを変数としてi+1を返すメソッド delegate ( int i ) { return i + 1 ; } は、ラムダ式を使って次のように記述できる:
( int i ) => i + 1 ; /* 式形式のラムダ */ //或いは: ( int i ) => { return i + 1 ; }; /* ステートメント形式のラムダ */ ラムダ式は匿名メソッドと同様に扱えるが、式形式のラムダがExpression<TDelegate>型として扱われた場合のみ匿名メソッドとして扱われず、コンパイラによって式木を構築するコードに変換される。匿名デリゲートが実行前にコンパイルされたCILを保持するのに対し、式木はCILに実行時コンパイル可能であるDOM のような式の木構造 そのものを保持する。これはLINQ クエリをSQLクエリなどに変換する際に役立つ。
以下は、3つの任意の名前の変数、整数、括弧、及び四則演算子のみで構成された式を逆ポーランド記法 に変換する汎用的なコードである:
public static string ToRPN ( Expression < Func < int , int , int , int >> expression ) { return Parse (( BinaryExpression ) expression . Body ). TrimEnd ( ' ' ); } private static string Parse ( BinaryExpression expr ) { string str = "" ; if ( expr . Left is BinaryExpression ) { str += Parse (( BinaryExpression ) expr . Left ); } else if ( expr . Left is ParameterExpression ) { str += (( ParameterExpression ) expr . Left ). Name + " " ; } else if ( expr . Left is ConstantExpression ) { str += (( ConstantExpression ) expr . Left ). Value + " " ; } if ( expr . Right is BinaryExpression ) { str += Parse (( BinaryExpression ) expr . Right ); } else if ( expr . Right is ParameterExpression ) { str += (( ParameterExpression ) expr . Right ). Name + " " ; } else if ( expr . Right is ConstantExpression ) { str += (( ConstantExpression ) expr . Right ). Value + " " ; } return str + expr . NodeType . ToString () . Replace ( "Add" , "+" ) . Replace ( "Subtract" , "-" ) . Replace ( "Multiply" , "*" ) . Replace ( "Divide" , "/" ) + " " ; } // 呼び出し例: ToRPN (( x , y , z ) => ( x + 1 ) * (( y - 2 ) / z )); // "x 1 + y 2 - z / *" を返す オブジェクトの初期化が式として簡潔に記述できるようになった。
var p = new Point { X = 640 , Y = 480 }; // 上の文は次のように解釈される: Point __p = new Point (); __p . X = 640 ; __p . Y = 480 ; Point p = __p ; また、コレクションの初期化も同様に簡潔に記述できるようになった。
var l = new List < int > { 1 , 2 , 3 }; var d = new Dictionary < string , int > {{ "a" , 1 }, { "b" , 2 }, { "c" , 3 }}; // 上の文は次のように解釈される: List < int > __l = new List < int > (); __l . Add ( 1 ); __l . Add ( 2 ); __l . Add ( 3 ); List < int > l = __l ; Dictionary < string , int > __d = new Dictionary < string , int > (); __d . Add ( "a" , 1 ); __d . Add ( "b" , 2 ); __d . Add ( "c" , 3 ); Dictionary < string , int > d = __d ; 但し、上のコードでは匿名の変数に便宜的に __p、__l、__d と命名している。実際はプログラマはこの変数にアクセスすることはできない。
プロパティをより簡潔に記述するための自動実装プロパティが導入された[ a 5] 。プロパティの定義にget; set;と記述することで、プロパティの値を保持するための匿名のフィールド(プログラマは直接参照することはできない)と、そのフィールドにアクセスするためのアクセサが暗黙に定義される。また、C# 5.0まではget;とset;のどちらか片方だけを記述することは出来なかったが、C# 6.0からはget;のみが可能。以下のコード:
は、以下のようなコードに相当する動作をする:
private int __value ; public int Value { get { return __value ; } set { __value = value ; } } 但し、上のコードでは匿名のフィールドに便宜的に__valueと命名している。実際はプログラマはこのフィールドにアクセスすることはできない。
一時的に使用される型を簡単に定義するための匿名型が導入された[ a 5] 。以下に例を挙げる:
new { Name = "John Doe" , Age = 20 } 上の式は、以下の内容のクラスを暗黙に定義する。定義されたクラスは匿名であるが故にプログラマは参照できない。
public string Name { get ; } public int Age { get ; } 同じ型、同じ名前のプロパティを同じ順序で並べた匿名型は同じであることが保証されている。即ち、以下のコード:
var her = new { Name = "Jane Doe" , Age = 20 } var him = new { Name = "John Doe" , Age = 20 } において、her.GetType() == him.GetType()はtrueである。
newキーワードを用いた配列の宣言の際、型を省略できるようになった。匿名型の配列を宣言する際に威力を発揮する。
var a = new [] { "foo" , "bar" , null }; // 上の文は次のように解釈される: string [] a = new string [] { "foo" , "bar" , null }; // 以下の文: var a = new [] { "foo" , "bar" , 123 }; // は次のように解釈されることなく、誤りとなる: object [] a = new object [] { "foo" , "bar" , 123 }; LINQ をサポートするために、クエリ式が導入された[ a 5] 。これはSQL の構文に類似しており、最終的に通常のメソッド呼び出しに変換されるものである。以下に例を示す:
var passedStudents = from s in students where s . MathScore + s . MusicScore + s . EnglishScore > 200 select s . Name ; 上のコードは以下のように変換される:
var passedStudents = students . Where ( s => s . MathScore + s . MusicScore + s . EnglishScore > 200 ) . Select ( s => s . Name ); C# 3.0で追加された構文の多くは式であるため、より巨大な式(当然クエリ式も含まれる)の一部として組み込むことができる。旧来複数の文に分けたり、作業用の変数を用意して記述していたコードを単独の式としてより簡潔に記述できる可能性がある。
出井秀行著の『実戦で役立つ C#プログラミングのイディオム/定石&パターン』(技術評論社、2017年)という書籍ではクエリ構文よりメソッド構文を推奨しており、クエリ構文ではLINQの全ての機能を使用できるわけではないこと、メソッド呼び出しは処理を連続して読める可読性があること、メソッド呼び出しであればMicrosoft Visual Studio の強力なインテリセンス が利用できることを理由に、著者はクエリ構文をほとんど使用していないと記している。
dynamicキーワードが導入され、動的型付け変数 を定義できるようになった[ a 5] 。dynamic型として宣言されたオブジェクトに対する操作のバインドは実行時まで遅延される。
// xはint型と推論される: var x = 1 ; // yはdynamic型として扱われる: dynamic y = 2 ; public dynamic GetValue ( dynamic obj ) { // objにValueが定義されていなくとも、コンパイルエラーとはならない: return obj . Value ; } VB やC++ に実装されているオプション引数・名前付き引数が、C#でも利用できるようになった[ a 5] 。
public void MethodA () { // 第1引数と第2引数を指定、第3引数は未指定: Console . WriteLine ( "Ans: " + MethodB ( 1 , 2 )); // Ans: 3 … 1 + 2 + 0となっている // 第1引数と第3引数を指定、第2引数は未指定: Console . WriteLine ( "Ans: " + MethodB ( A : 1 , C : 3 )); // Ans: 4 … 1 + 0 + 3となっている } // 引数が指定されなかった場合のデフォルト値を等号で結ぶ: public int MethodB ( int A = 0 , int B = 0 , int C = 0 ) { return A + B + C ; } ジェネリクスの型引数に対してin、out修飾子を指定することにより、ジェネリクスの共変性・反変性を指定できるようになった[ a 5] 。
IEnumerable < string > x = new List < string > { "a" , "b" , "c" }; // IEnumerable<T>インターフェイスは型引数にout修飾子が指定されているため、共変である。 // したがって、C# 4.0では次の行はコンパイルエラーにならない IEnumerable < object > y = x ; 静的usingディレクティブを利用することで、型名の指定無しに他クラスの静的メンバーの呼び出しを行えるようになった。利用するにはusing staticの後に完全修飾なクラス名を指定する。
using static System . Math ; // ↑ソースコードの上部で宣言 class Hogehoge { // System.Math.Pow() , System.Math.PI を修飾無しで呼び出す double area = Pow ( radius , 2 ) * PI ; } catchの後にwhenキーワードを使用することで、処理する例外を限定することができるようになった。
try { // ... } catch ( AggregateException ex ) when ( ex . InnerException is ArgumentException ) { // ... } 出力変数宣言 パターンマッチング(is式/switch文) タプル(タプル記法/分解/値の破棄) ローカル関数 数値リテラルの改善(桁セパレータ/バイナリリテラル) ref戻り値、ref変数 非同期戻り値型の汎用化 Expression-bodied機能の拡充(コンストラクタ/デストラクタ/get/set/add/remove) Throw式 out引数で値を受け取る場合、その場所で変数宣言可能となった[ a 38] 。
total += int . TryParse ( "123" , out var num ) ? num : 0 ; is式の構文が拡張され、型の後ろに変数名を宣言できるようになった[ a 38] 。拡張されたis式はマッチした場合に宣言した変数にキャストした値を代入し、さらにtrue と評価される。マッチしなかった場合はfalse と評価され、宣言した変数は未初期化状態となる。
void CheckAndSquare ( object obj ) { // objの型チェックと同時にnumに値を代入する。 if ( obj is int num && num >= 0 ) { num = num * num ; } else { num = 0 ; } // if文の条件セクションは、ifの外側と同じスコープ Console . WriteLine ( num ); } switch文のマッチ方法が拡張され、caseラベルに従来の「定数パターン」に加え、新たに「型パターン」を指定できるようになった。また、「型パターン」のcaseラベルでは、when句に条件を指定することができる。「型パターン」を含むswitch文では、必ずしも条件が排他的でなくなったため、最初にマッチしたcaseラベルの処理が実行される。[ a 39]
void Decide ( object obj ) { switch ( obj ) { case int num when num < 0 : Console . WriteLine ( $"{num}は負の数です。" ); break ; case int num : Console . WriteLine ( $"{num}を二乗すると{num * num}です。" ); break ; case "B" : Console . WriteLine ( $"これはBです。" ); break ; case string str when str . StartsWith ( "H" ): Console . WriteLine ( $"{str}はHから始まる文字列です。" ); break ; case string str : Console . WriteLine ( $"{str}は文字列です。" ); break ; case null : Console . WriteLine ( $"nullです" ); break ; default : Console . WriteLine ( "判別できませんでした" ); break ; } } タプル のための軽量な構文が導入された[ a 38] 。従来のSystem.Tupleクラスとは別に、System.ValueTuple構造体が新しく追加された。
2個以上の要素を持つタプルのための記法が導入された。引数リストと同様の形式で、タプルを記述できる。
// タプル記法 ( int , string ) tuple = ( 123 , "Apple" ); Console . WriteLine ( $"{tuple.Item1}個の{tuple.Item2}" ); 多値戻り値を簡単に扱えるように、分解がサポートされた[ a 38] 。
var tuple = ( 123 , "Apple" ); // 分解 ( int quantity , string name ) = tuple ; Console . WriteLine ( $"{quantity}個の{name}" ); 分解はタプルに限らない。Deconstruct()メソッドが定義されたクラスでも、分解を利用できる[ a 38] 。
以下に、DateTime型に分解を導入する例を示す。
static class DateExt { public static void Deconstruct ( this DateTime dateTime , out int year , out int month , out int day ) { year = dateTime . Year ; month = dateTime . Month ; day = dateTime . Day ; } } 上記のコードでDateTime型にDeconstruct()拡張メソッドを定義し、
// 分解 ( int year , int month , int day ) = DateTime . Now ; のように左辺で3つの変数に値を受け取ることができる。
分解、out引数、パターンマッチングで、値の破棄を明示するために_が利用できるようになった。破棄された値は、後で参照することはできない。
// 年と日は使わない ( _ , int month , _ ) = DateTime . Now ; // 解析結果だけ取得し、変換された値は使わない bool isNumeric = int . TryParse ( str , out _ ); switch ( obj ) { // string型で分岐するが、値は使わない case string _ : // Do something. break ; } refキーワードの使用方法が拡張された。これによって、安全な参照の使い道が広がった。
戻り値の型をrefで修飾することで、オブジェクトの参照を戻り値とすることができる。
// 二つの参照引数の内、値の大きいものの参照戻り値を返す static ref int Max ( ref int left , ref int right ) { if ( left >= right ) { return ref left ; } else { return ref right ; } } 変数の寿命は変わらないため、メソッド終了時に破棄されるローカル変数をref戻り値とすることはできない。
static int s_count = 1 ; // メンバーの参照はref戻り値になる。 static ref int ReturnMember () { return ref s_count ; } // ref引数はもちろんref戻り値になる。 static ref int ReturnRefParam ( ref int something ) { return ref something ; } // ローカル変数をref戻り値とすることはできない。 // static ref int ReturnLocal() { // int x = 1; // return ref x; // } ローカル変数の型をrefで修飾することで、参照を代入することができる。
// 参照戻り値を参照変数で受け取る ref int max = ref Max ( ref x , ref y ); // limitとmaxは同じ値を参照する ref int limit = ref max ; Mainメソッドの戻り値として、Task型、Task(int)型が認められた[ a 40] 。
型推論可能な場面では、defaultの型指定は省略可能となった[ a 40] 。
int number = default ; string name = default ; C#7.2で追加された仕様は以下の通り[ a 41] [ a 42] 。
値型におけるパフォーマンス向上を意図した複数の機能が追加された。
in参照渡し、ref readonly参照戻り値[ 編集 ] 引数にinを指定することで、読み取り専用参照渡しを指定できる。また、戻り値にref readonlyを指定することで、読み取り専用参照戻り値を指定できる。
これにより、構造体のコピーを避けると共に、意図しない値の変更を抑止できる。
構造体宣言時にreadonlyを指定することで、真の読み取り専用構造体を定義できる。readonly構造体の全てのフィールドはreadonlyでなければならず、thisポインタも読み取り専用となる。
これにより、メンバーアクセス時の意図しない防御的コピーを抑止できる。
構造体宣言時にrefを指定することで、ヒープ領域 へのコピーを防ぐ構造体がサポートされる。ref構造体では、box化できない、配列を作成できない、型引数になることができない、など、ヒープ領域へのコピーを防ぐための厳しい制限がかかる。
この機能は、Span<T>のような構造体をサポートするために利用され、unsafe文脈以外でのstackallocの利用をも可能とする。
C#4.0で追加された名前付き引数 が末尾以外でも利用できるようになった。
Hogehoge ( name : "John" , 17 ); private protected アクセス修飾子[ 編集 ] 同一アセンブリ内、かつ、継承先からのアクセス許可を表すprivate protectedアクセス修飾子が追加された。
十六進リテラルの0x、二進リテラルの0bの直後のアンダースコアが認められた。
int bin = 0 b_01_01 ; int hex = 0 x_AB_CD ; C#7.3では以下の仕様が追加された[ a 43] 。
ジェネリック型制約の種類の追加System.Enum、System.Delegateunmanaged(文脈キーワード) unsafe class MyGenericsClass < T1 , T2 , T3 > where T1 : System . Enum where T2 : System . Delegate where T3 : unmanaged { public MyGenericsClass ( T1 enum1 , T1 enum2 , T2 func , T3 unmanagedValue ) { if ( enum1 . HasFlag ( enum2 )) { func . DynamicInvoke (); } else { T3 * ptr = & unmanagedValue ; } } } refローカル変数の再割り当てstackalloc初期化子Indexing movable fixed buffers カスタムfixedステートメント オーバーロード解決ルールの改善 出力変数宣言の利用箇所の追加 class MyOutVar { // メンバー変数初期化子やコンストラクタ初期化子で出力変数宣言が可能 readonly int x = int . TryParse ( "123" , out var number ) ? number : - 1 ; } ( long , long ) tuple = ( 1L , 2L ); // タプルのすべての要素間で == が比較可能 if ( tuple == ( 1 , 2 )) { } // 要素数が異なるタプル同士は比較できない。 //if (tuple == (1, 2, 3)) { } バッキングフィールドに対するAttribute指定 // C#7.2までは無効な指定(コンパイル自体は可能。無視される) // C#7.3からはバッキングフィールドに対するAttribute指定と見なされる [field: NonSerialized] public int MyProperty { get ; set ; } C# 8.0で追加された仕様は以下の通り。[ a 44] [ 19]
参照型にnull許容性を指定できるようになった。参照型の型名に?を付加した場合にnull許容参照型となる。
参照型の型名に?を付加しない場合、null非許容参照型となる。
フロー解析レベルでのnull許容性チェックが行われる。null許容値型のNullable<T>のような新しい型は導入されない。
参照型のnull許容性は、null許容コンテキストによって有効、無効の切り替えが可能である。C#7.3以前の互換性のために、既定では無効となっている。
Nullable コンパイルオプション: プロジェクト全体でのnull許容コンテキストを指定する #nullable ディレクティブ: ソースコードの部分ごとにnull許容コンテキストを指定するannotations オプション、warningsオプションにより、適用範囲を限定できるnull許容参照型の変数名の後に!を使用することで、フロー解析時の警告が免除される。
インタフェースのメンバーに既定の実装を指定できるようになった。また、インタフェースに静的メンバーを持つことができるようになった。
さらに、インタフェースのメンバーにアクセシビリティを指定できるようになった。
既定のアクセシビリティは、従来通りpublic となる。 実装があるインスタンスメンバーは、既定でvirtual となりoverride可能である。 実装をoverrideさせないためにsealedを指定することができる。 パターンマッチングの拡張 (C# 8.0)[ 編集 ] switch式が追加された。プロパティパターン、タプルパターン、位置指定パターンの追加により、再帰的なパターンマッチングが可能になった。
IAsyncEnumerable<T>インタフェース を返すことで、イテレータ構文と非同期構文の共存が可能になった。
async IAsyncEnumerable < int > EnumerateAsync () { await Task . Delay ( 100 ); yield return 1 ; await Task . Delay ( 100 ); yield return 2 ; } await foreachによって非同期ストリームを列挙する。
async void SpendAsync () { await foreach ( var item in EnumerateAsync ()) { Console . WriteLine ( item ); } } IndexとRangeを指定できる専用構文が追加された。
Index a = 1 ; // new Index(1, fromEnd: false) Index b = ^ 1 ; // new Index(1, fromEnd: true) Range range = a .. b ; // new Range(start: a, end: b) 静的ローカル関数 null結合代入演算子 構造体の読み取り専用メンバー using 宣言 ref構造体のDispose ジェネリクスを含むアンマネージ型 式中のstackalloc 文字列補間のトークン順序の緩和 C# 9.0で追加された仕様は以下の通り。
[ a 45]
レコード プロパティのinitアクセサ 最上位レベルステートメント パターンマッチングの拡張 new式の型推論 条件演算子の型推論 共変戻り値 GetEnumeratorの拡張メソッド対応 静的匿名関数 ラムダ式引数の破棄 ローカル関数への属性適用 パフォーマンスと相互運用ネイティブサイズの整数型(nint型、nuint型) 関数ポインタ(delegate*型) 変数初期化フラグの抑制 コードジェネレータのサポート C# 10.0で追加された仕様は以下の通り。
[ a 46]
レコード構造体 構造体型の機能強化 補間された文字列ハンドラー global using ディレクティブ ファイル スコープの名前空間の宣言 拡張プロパティのパターン ラムダ式の機能強化 const 補間文字列を許可する レコードの型で ToString() を封印できる 限定代入の機能強化 同じ分解で代入と宣言の両方を許可する メソッドで AsyncMethodBuilder 属性を許可する CallerArgumentExpression 属性 拡張 #line pragma 警告ウェーブ 6 C# 11.0で追加された仕様は以下の通り[ a 15] [ b 6] 。
エスケープなどの加工を施さない生の文字列を、3個以上の二重引用符 で括る事で表現できる様になった[ b 12] 。未加工の文字列リテラル とも呼ばれる。生文字列中には、その文字列を括っている二重引用符より少ない数で連続した二重引用符を普通の文字として埋め込む事ができる。文字列の開始を示す二重引用符の直後に何らかの文字列が記述されているかどうかで単一行か複数行かが弁別される。
単一行の場合は、"""あいうえお"かきくけこ"さしすせそ"""の様に記述でき、これはあいうえお"かきくけこ"さしすせそ を表す。文字列中に3個の二重引用符を含める場合は""""たちつてと"""なにぬねの""""などと書ける。
複数行の場合において開始を示す二重引用符の直後は、空白または改行のみが受け付けられ、次の行が実際の一行目となる。複数行の生文字列の行はインデントする事ができ、文字列の終了を示す二重引用符のインデント位置と同等の空白文字とタブ文字が全ての行から削除される。その為、終了を示す二重引用符は、最終行の次の行にインデントと伴に置かなければならず、途中の行をこのインデント位置より浅くするとコンパイルエラーになる[ b 12] 。
複数行の生文字列の記述例は次の通り。
string logMsg = """ 原因不明のエラ ー が発生しました。 詳細はログファイル "C:\Logs\exception.log" を確認してください。 """; 開始を示す二重引用符の直前に任意の数の$を置く事で、補間文字列の機能を用いる事もできる。式を埋め込むには$と同数の半角波括弧 ({と})で括らなければならない。$より少ない数で連続した波括弧は普通の文字として扱われ、$より多い数で連続させるとコンパイルエラーになる[ b 12] 。
属性の型が型引数を持てる様になった。
// 属性 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public class ConverterContractAttribute < TFrom , TTo > : Attribute { } // 使用例 [ConverterContract<byte, string>()] [ConverterContract<sbyte, string>()] [ConverterContract<short, string>()] [ConverterContract<ushort, string>()] [ConverterContract<int, string>()] [ConverterContract<uint, string>()] [ConverterContract<long, string>()] [ConverterContract<ulong, string>()] public class IntToStringConverter { // ... } パターンマッチングの拡張 (C# 11.0)[ 編集 ] リストや配列に対するパターンマッチが可能になった[ a 47] [ b 13] 。
int [] nums = new [] { 0 , 1 , 2 , 3 , 4 , 5 }; if ( nums is [ 0 , 1 , 2 , .. ]) { Console . WriteLine ( "配列は 0, 1, 2 から始まります。" ); } else { Console . WriteLine ( "配列は 0, 1, 2 から始まりません。" ); } また、Span<char>やReadOnlySpan<char>に対して文字列を用いたパターンマッチが可能になった。
bool CheckSignature ( ReadOnlySpan < char > sig ) => sig is "HOGE" ; 型引数に「数値型または数値型に類似している型」である事を示す制約を付け加える機能が導入された[ a 48] [ b 14] 。また、それに呼応して下記の変更が行われた。
明示的な論理シフト演算子(>>>演算子)が追加された。 シフト演算子の右側の引数の型に対する制限の撤廃され、int型以外の型を指定できる様になった。 checked演算子のオーバーロードができる様になった。インターフェースのメソッドに対してstatic abstractキーワード(静的抽象)とstatic virtualキーワード(静的仮想)を指定できる様になった[ a 49] [ a 50] 。 インターフェース内に演算子を定義できる様になった。 ジェネリック型数値演算を用いた一例を下記に示す[ a 51] [ a 52] 。
// 大抵の演算子インターフェイスは System.Numerics 内に実装されている。 using System.Numerics ; // 任意の型に対して加算を行う事ができる関数。 static T MyAdd < T > ( T value1 , T value2 ) where T : IAdditionOperators < T , T , T > // 加算が可能な型のみを受け付ける制約。 => value1 + value2 ; // + 演算子を使う事ができる。 // 上記の関数定義のみで、下記の様に加算演算が定義された型であれば、任意の型で呼び出す事ができる。 int a = MyAdd ( 123 , 456 ); // 結果:579 ulong b = MyAdd ( 111UL , 222UL ); // 結果:333 double c = MyAdd ( 1.5D , 2.1D ); // 結果:3.6 UTF-8 文字列リテラル 文字列補間式の改行 ファイルローカル型 必須メンバー auto-default 構造体(構造体の未初期化のフィールド変数が自動的に既定値に初期化される) nameofのスコープ拡張IntPtrに別名nint、UIntPtrに別名nuintが付いたrefフィールドscoped ref変数メソッドグループからデリゲートへの変換の改善 警告ウェーブ 7 C# 12.0で追加された仕様は以下の通り[ a 18] [ b 7] 。
クラスと構造体のプライマリコンストラクター[ 編集 ] レコード型(record)以外のクラス(class)と構造体(struct)でプライマリコンストラクターが使えるようになった。
class Example ( string message ) { public string Message { get ; } = message ; } 配列、コレクション、Span<T>などの初期化の記法が共通の記法([])で書けるようになった。
// Create an array: int [] a = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]; // Create a list: List < string > b = [ "one" , "two" , "three" ]; // Create a span Span < char > c = [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'h' , 'i' ]; コレクション式で複数のコレクションをインライン展開できる新しい演算子(..)が追加された。
int [] row0 = [ 1 , 2 , 3 ]; int [] row1 = [ 4 , 5 , 6 ]; int [] row2 = [ 7 , 8 , 9 ]; // 1, 2, 3, 4, 5, 6, 7, 8, 9, int [] single = [.. row0 , .. row1 , .. row2 ]; インライン配列 既定のラムダパラメーター 任意の型の別名設定 ref readonlyパラメーター試験段階の属性 インターセプター C# 13.0で追加された仕様は以下の通り[ a 22] [ b 8] 。
エスケープ文字 [ 注釈 6] を表すエスケープシーケンスとして\eが追加された。これに従って\x1bの使用は非推奨となった。
オブジェクト初期化子においても^演算子を用いたアクセスが可能となった。これにより、初期化子内でも配列やリストなどの末尾から値へアクセスできる。
コレクション式に対応している型であれば、引数にparams修飾子を付けられる様になった[ b 15] 。
// 関数定義 void Print ( string format , params IEnumerable < object > args ) { /* ... 任意の実装 ... */ // 例えば Console . WriteLine ( format , args . ToArray ()); } // 呼び出し int a = 2 , b = 3 ; Print ( "{0} + {1} = {2}" , a , b , a + b ); プロパティとインデクサにもpartial修飾子の指定が可能となった[ a 53] 。
public partial bool IsThisPartialProperty { get ; } public partial char this [ int index ] { get ; } 任意のオブジェクトで排他制御される問題への対策として、lock文専用のLockクラスが用意された[ b 16] 。以下の様に使う。
var lockObj = new Lock (); lock ( lockObj ) { // 排他制御されるコード } // このコードは、コンパイラによって次の形に展開される: using ( lockObj . EnterScope ()) { // 排他制御されるコード } メソッド グループでの自然型 ref 構造体のインターフェイス実装OverloadResolutionPriority 属性(オーバーロードの解決優先度を変更できる)イテレーター・非同期メソッド内のref/unsafe インターセプター コレクション式の改善 C# 14.0で追加された仕様は以下の通り[ a 25] [ b 9] 。
新たにextension ブロックが導入され、拡張プロパティと拡張インデクサーを実装できる様になった[ a 54] [ a 55] 。この機能は、拡張メンバー [ a 54] (英 :extension members [ a 56] )または拡張宣言 [ a 57] (英:extension declarations [ a 58] )などと呼称される。特定の型に対してメンバーを外部の型から後付する事ができる。型引数を受け取るには、extension キーワードの後ろに<T> と記述する。また、動的メンバーだけではなく静的メンバーを追加する事もできる。
整数型に新たなプロパティを追加するコードを以下に示す。
static class Some { extension ( int arg ) { public int Twice => arg << 1 ; public int Half => arg >> 1 ; } } プロパティのアクセサー内でfield キーワードを用いる事でバッキングフィールドを自動生成できる[ a 59] [ a 60] 。従ってプロパティ専用の変数を新たに定義する必要は無い。既にfield という名称の変数が存在する場合、代わりに@field[ 注釈 7] またはthis.field[ 注釈 8] と記述しなければならない。
get アクセサーのみで用いる場合は以下の様に記述できる。
class Some { public object? Data { get => field ?? new (); set ; } } 対して、set アクセサーのみで用いる場合は以下の様に記述できる。
class Some { public int Number { get ; set => value = field + 1 ; } } 両方のアクセサーで同時に用いる事も、set の代わりにinit 内で用いる事も、従来通りget; set; と記述する事もできる。バッキングフィールドに属性を付与する場合は、プロパティ宣言の直前に[field: AttributeClassName()] と記述する。
null 条件演算子を代入演算子の左辺でも用いられる様になった[ a 61] [ a 62] 。
例えば、次の様なクラスが定義されているものとする。
class Some { public string? Name { get ; set ; } } 通常は、if 文を用いてnull ではない場合にのみ代入処理が実施される様にしなければならない。
void SetName ( Some ? obj , string? name ) { if ( obj is not null ) { obj . Name = name ; } } 新しい演算子によって、変数の検証を略記できる様になった。
void SetName ( Some ? obj , string? name ) { obj ?. Name = name ; } 複合代入演算子の動作を上書きできる様になった[ a 63] 。コピーコストの削減が目的である[ b 17] 。他の演算子と同じ書式で定義できる。
public void operator += ( int arg ) { /* ... ここに具体的なコードを書く ... */ } nameof の拡張(引数無しのジェネリック型にも対応)暗黙的なSpan<T> とReadOnlySpan<T> の変換 ラムダ式の引数の修飾子(主に参照渡し関係) partial の拡張(部分コンストラクタと部分イベント)C#の言語仕様は標準化団体Ecma International を通じて公開・標準化されており、第三者がマイクロソフトとは無関係にコンパイラや実行環境を実装することができる[ 2] [ 20] 。
マイクロソフト.NET Runtime .NET Framework Runtime Mono Project カプコン ECMA-334 3rd/4th/5th edition によると、C# は「C Sharp (シーシャープ)」と発音し、LATIN CAPITAL LETTER C (U+0043) の後にNUMBER SIGN # (U+0023) と書く[ 22] 。MUSIC SHARP SIGN ♯ (U+266F) ではなくNUMBER SIGN # (U+0023) を採用したのは、フォント やブラウザ などの技術的な制約に加え、ASCII コードおよび標準的キーボードには前者の記号が存在しないためである。
「#」を接尾辞とする名称は、他の .NET 言語にも使用されており、J# (Javaのマイクロソフトによる実装)、A# (英語版 ) (Ada から)、F# (System F などから[ 23] )が含まれる。更には、Gtk# (GTK などのGNOME ライブラリの .NET ラッパ)、Cocoa# (英語版 ) (Cocoa のラッパ)などのライブラリにも使用されている。そのほか、SharpDevelop などの「Sharp」を冠する関連ソフトウェアも存在する。
C# という名称の解釈として、「(A-Gで表された)直前の音を半音上げる」という音楽記号の役割に着目し、「C言語を改良したもの」を意味したのではないか、というものがある [要出典 ] 。これは、C++の名称が「C言語を1つ進めたもの」という意味でつけられたことにも似ている。アンダース・ヘルスバーグ は、「C#」が「C++++」(すなわち「C++ をさらに進めたもの」)にみえるのが由来である、と語っている[ 24] [ 25] 。
^ 全てではなく一部にプロプライエタリなコンポーネントもある。そのため、それに依存するものなど、後述のMonoなどで制限がある場合がある[ 2] 。 ^ 「ECMA-334」、「ISO/IEC 23270:2003」、「JIS X 3015」として標準化している。 ^ .NET Framework 2.0と3.0はLINQを除く[ 5] 。 ^ C# 9.0 以降のマイクロソフトの仕様書は機能仕様 にまとめられている。標準仕様 ではなくマイクロソフト独自の仕様として扱われている。 ^ この機能はglobal using とも呼ばれる。 ^ Unicode の符号位置 でU+001Bとなる文字。^ 変数名だけではなく型名や関数名などにも用いる事ができる。 ^ クラスまたは構造体のメンバーを参照する場合のみ。 ^ 岩崎宰守「Microsoft、開発フレームワーク「.NET Core 1.0」「ASP.NET Core 1.0」「EF Core 1.0」を提供開始 」『INTERNET Watch 』2016年6月28日。2025年11月20日閲覧 。 ^a b Abel Avram (2009年7月29日). “誰でもC#とCLIの正式な実装が可能に ”. InfoQ . 2019年12月2日閲覧。 ^ “Standard ECMA-334 ”. ECMA. 2023年12月23日閲覧。 ^ “Standard ECMA-334-archive ”. 2018年11月13日時点のオリジナル よりアーカイブ。2018年11月13日閲覧。 ^ “Using C# 3.0 from .NET 2.0 ”. Danielmoth.com (2007年5月13日). 2012年10月4日閲覧。 ^ “Microsoft、「.NET Framework 4.6」を正式公開” . 窓の杜 . https://forest.watch.impress.co.jp/docs/news/712658.html 2021年1月23日閲覧。 ^ “.NET Framework 4.7が一般公開される” . InfoQ . https://www.infoq.com/jp/news/2017/05/net47-released/ 2021年1月23日閲覧。 ^ “Micorsoftが.NET Core 3.0をリリース” . InfoQ . https://www.infoq.com/jp/news/2019/12/microsoft-releases-dotnet-core-3/ 2021年1月23日閲覧。 ^ “IL2CPP の概要 - Unity マニュアル ”. Unity. 2023年12月23日閲覧。 ^ “Burst の概要 ”. Unity. 2023年12月23日閲覧。 ^a b 高橋 2005 , p. 70. ^ 高橋 2005 , pp. 63–64.^ 高橋 2005 , pp. 70, 71.^ 高橋 2005 , p. 68.^ 高橋 2005 , pp. 68–70.^ 高橋 2005 , pp. 66, 67.^ 高橋 2005 , p. 71.^ 高橋 2005 , p. 72.^ “MicrosoftがC# 8.0をリリース ”. InfoQ (2019年12月10日). 2019年12月12日閲覧。 ^ Tim Smith (2010年10月4日). “Javaと.NETの特許問題への短い紹介 ”. InfoQ . 2019年12月2日閲覧。 ^ “ゲームプログラムのC#8.0/.NET対応とその未来 ”. CAPCOM. 2024年6月29日閲覧。 ^ Standard ECMA-334 C# Language Specification ^ The A-Z of programming languages: F# | Network World ^ レポート:コミュニティスペシャルセッション with Anders Hejlsberg in Microsoft Developers Conference 2006 ^ C#への期待。アンダースからの返答 高橋 忍「C# 2.0の新しい言語仕様」『C MAGAZINE(2005年2月号)』第17巻第2号、ソフトバンク パブリッシング。 山田祥寛『独習C#』(第5版)翔泳社〈独習〉、2022年7月21日。ISBN 978-4-7981-7556-0 。 低水準言語 高水準言語
1950年代 1960年代 1970年代 1980年代 1990年代 2000年代 2010年代 2020年代
架空の言語