C Sharp
C#(シーシャープ)は、マイクロソフトが開発した、汎用のマルチパラダイムプログラミング言語である。C#は、Javaに似た構文を持ち、C++に比べて扱いやすく、プログラムの記述量も少なくて済む。また、C#は、Windowsの.NET Framework上で動作することを前提として開発された言語であるが、2023年現在はクロスプラットフォームな.NETランタイム上で動作する。デスクトップ・モバイルを含むアプリケーション開発や、ASP.NETをはじめとするWebサービスの開発フレームワーク、ゲームエンジンのUnityでの採用事例などもある。 概要開発にはボーランドのTurbo PascalやDelphiを開発したアンダース・ヘルスバーグを筆頭として多数のDelphi開発陣が参加している。 構文はC系言語(C言語、C++、Javaなど)の影響を受けており、その他の要素には、以前、ヘルスバーグが所属していたボーランド設計のDelphiの影響が見受けられる。また、主要言語へのasync/await構文や、ヘルスバーグが言語設計に関わるTypeScriptでのジェネリクス採用など、他言語への影響も見られる。 C#は共通言語基盤(共通言語ランタイムなど)が解釈する共通中間言語にコンパイルされて実行される。 また、C#はマルチパラダイムをサポートする汎用高レベルプログラミング言語で、静的型付け、タイプセーフ、スコープ、命令型、宣言型、関数型、汎用型、オブジェクト指向(クラスベース)、コンポーネント指向のプログラミング分野を含んでいる。他にも自動ボックス化、デリゲート、 プロパティ、インデクサ、カスタム属性、ポインタ演算操作、構造体(値型オブジェクト)、多次元配列、可変長引数、async/await構文、null安全などの機能を持つ。また、Javaと同様に大規模ライブラリ、プロセッサ・アーキテクチャに依存しない実行形態、ガベージコレクション、JITコンパイルによる実行の高速化、AOTコンパイラによる高速実行、などが実現されている(もっともこれらはC#の機能というより.NET によるものである)。 共通言語基盤 (CLI) といった周辺技術も含め、マイクロソフトのフレームワークである「.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やVisual C++ (C++/CLI) など)と相互に連携することができる。 バージョンおよびリリース時期
言語仕様さまざまな意味において、基盤であるCLIの機能をもっとも反映している言語であるといえる。C#にある組み込み型のほとんどは、CLIフレームワークに実装されている値型と対応している。 しかし、C#の言語仕様はコンパイラのコード生成については何も言及していないため、CLRに対応しなければならないとか、共通中間言語 (CIL) などの特定のフォーマットのコードを生成しなければならないとかいうことは述べられていない。 そのため、理論的にはC++やFORTRANのように環境依存のマシン語を生成することも可能である。しかし、現在存在するすべてのC#コンパイラはCLIをターゲットにしている。 .NET 7.0以降で可能になった事前コンパイルの一種である「Native AOT」でデプロイすることで実行可能な環境依存のバイナリを出力することが可能である。しかしながらこの手法もCLIとランタイムを事前に各アーキテクチャ向けのバイナリに変換しているだけであり、CLIを経由することに変わりはない。[a 26] 特殊な例としては、UnityのScripting Backendである「IL2CPP」[8]や「Burst」[9]がある。 IL2CPPはC#をコンパイルしたCILをさらにC++コードへと変換後、ネイティブバイナリへC++コンパイラによってコンパイルされる。BurstはC#をコンパイルしたCILをLLVMコンパイラによってネイティブバイナリへコンパイルするものである。 Hello World最新のC#ではHello Worldを下記の通りに1行で記述できる[a 27]。 Console.WriteLine("Hello World!");
上記のコードは、コンパイラによって下記の様なコードに展開される。 using System;
namespace Wikipedia
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
次の言語機能によって実現されている。ただし、未対応の古いコンパイラを用いる場合は、省略できない。 CやC++からの改良点C#では、CやC++と比較してさまざまな制限や改良が加えられている。また、仕様の多くはC#言語というよりは、基盤である .NET そのものに依拠している。Javaで導入された制限および改良をC#でも同様に採用しているものが多いが、C#で新たに導入された改良がのちにJavaにも同様に採用されたものもある。その例を次に挙げる。 構文や構文以外の改良点
ポインタとメモリ管理
名前空間とオブジェクト指向な型システム
C# 2.0からの仕様部分型部分型 (Partial Type) が導入された[a 4]。以下のようにクラスや構造体の宣言に partial class MyClass { int a; }
partial class MyClass { int b; }
これは以下と同義である: class MyClass { int a; int b; }
これによって、巨大なクラスを分割したり、自動生成されたコードを分離したりすることができる。 ジェネリクスジェネリクスが導入された[a 4]。これは.NET Framework 2.0の機能である。クラス、構造体、インタフェース、デリゲート、メソッドに対して適用することができる。 .NETのGenericsはC++のテンプレート、あるいはJavaにおけるそれとも異なるもので、コンパイルによってではなく実行時にランタイムによって特殊化される。これによって異なる言語間の運用を可能にし、リフレクションによって型パラメーターに関する情報を取得することができる。また、 静的クラス静的クラスが導入された[a 4]。 イテレータイテレータ#C# 2.0を参照。 yieldキーワード
匿名デリゲートプロパティに対する個別のアクセス制御Property Accessors
プロパティの public class MyClass
{
private string status = string.Empty;
public string Status
{
get { return status; }
private set { status = value; }
}
}
Null許容型とnull結合演算子nullを保持できる値型、 int? i = 512;
i = null;
int? j = i + 500; //jはnullとなる。nullとの演算の結果はnullになる。
int? x = null;
object o = x;
System.Console.WriteLine(o == null); //Trueが出力される
また、null結合演算子 ( object obj1 = null;
object obj2 = new object();
object obj3 = new object();
return obj1 ?? obj2 ?? obj3; // obj2 を返す
この演算子は主に int? i = null;
int j = i ?? -1; // nullをint型に代入することはできない
→「Null合体演算子 § C#」も参照
その他
C# 3.0からの仕様varキーワード
var s = "foo";
// 上の文は右辺が string 型であるため、次のように解釈される:
string s = "foo";
// 以下に挙げる文は誤りである(コンパイルエラーとなる):
var v; // 初期化式を欠いている (型を推論する対象が存在しない)
var v = null; // 型が推論できない (曖昧である)
拡張メソッド拡張メソッド (extension method) が導入された[a 4]。既存のクラスを継承して新たなクラスを定義することなく、新たなインスタンスメソッドを疑似的に追加定義することができる。具体的には、入れ子になっていない、非ジェネリックの静的クラス内に、 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);
}
}
この例は、文字列( // 静的メソッドとしての呼び出し
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 34]。 部分メソッド部分メソッドが導入された[a 4]。部分型( partial class Class
{
partial void DebugOutput(string message);
void Method()
{
DebugOutput("Some message");
Console.WriteLine("Did something.");
}
}
上のコードにおいて partial class Class
{
partial void DebugOutput(string message)
{
Console.Write("[DEBUG: {0}] ", message);
}
}
を追加した上で ラムダ式ラムダ式が導入された[a 4]。この名前はラムダ計算に由来する。 以下の匿名メソッド // iを変数としてi+1を返すメソッド
delegate (int i) { return i + 1; }
は、ラムダ式を使って次のように記述できる: (int i) => i + 1; /* 式形式のラムダ */
//或いは:
(int i) => { return i + 1; }; /* ステートメント形式のラムダ */
ラムダ式は匿名メソッドと同様に扱えるが、式形式のラムダが 以下は、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 4]。プロパティの定義に public int Value { get; set; }
は、以下のようなコードに相当する動作をする: private int __value;
public int Value
{
get { return __value; }
set { __value = value; }
}
但し、上のコードでは匿名のフィールドに便宜的に 匿名型一時的に使用される型を簡単に定義するための匿名型が導入された[a 4]。以下に例を挙げる: 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 }
において、 配列宣言の型省略
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 4]。これは 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の強力なインテリセンスが利用できることを理由に、著者はクエリ構文をほとんど使用していないと記している。 C# 4.0からの仕様dynamicキーワードdynamicキーワードが導入され、動的型付け変数を定義できるようになった[a 4]。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 4]。 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 4]。 IEnumerable<string> x = new List<string> { "a", "b", "c" };
// IEnumerable<T>インターフェイスは型引数にout修飾子が指定されているため、共変である。
// したがって、C# 4.0では次の行はコンパイルエラーにならない
IEnumerable<object> y = x;
C# 5.0からの仕様
非同期処理 (async/await)→「Async/await」を参照
C# 6.0からの仕様
静的 using ディレクティブ静的 using ディレクティブを利用することで、型名の指定無しに他クラスの静的メンバーの呼び出しを行えるようになった。利用するには using static System.Math;
// ↑ソースコードの上部で宣言
class Hogehoge {
// System.Math.Pow() , System.Math.PI を修飾無しで呼び出す
double area = Pow(radius, 2) * PI;
}
例外フィルタ
try {
// ...
}
catch (AggregateException ex) when (ex.InnerException is ArgumentException) {
// ...
}
C# 7.0からの仕様
出力変数宣言
total += int.TryParse("123", out var num) ? num : 0;
パターンマッチングis 式の拡張
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 文の拡張
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 36]。従来の タプル記法2個以上の要素を持つタプルのための記法が導入された。 引数リストと同様の形式で、タプルを記述できる。 // タプル記法
(int, string) tuple = (123, "Apple");
Console.WriteLine($"{tuple.Item1}個の{tuple.Item2}");
分解多値戻り値を簡単に扱えるように、分解がサポートされた[a 36]。 var tuple = (123, "Apple");
// 分解
(int quantity, string name) = tuple;
Console.WriteLine($"{quantity}個の{name}");
分解はタプルに限らない。 以下に、 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;
}
}
上記のコードで // 分解
(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変数
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;
C# 7.1からの仕様非同期なMainメソッドMainメソッドの戻り値として、 static Task Main()
static Task<int> Main()
default式型推論可能な場面では、 int number = default;
string name = default;
C# 7.2からの仕様C#7.2で追加された仕様は以下の通り[a 39][a 40]。 値型の参照セマンティクス値型におけるパフォーマンス向上を意図した複数の機能が追加された。 in参照渡し、ref readonly参照戻り値引数に これにより、構造体のコピーを避けると共に、意図しない値の変更を抑止できる。 readonly構造体構造体宣言時に これにより、メンバーアクセス時の意図しない防御的コピーを抑止できる。 ref構造体構造体宣言時に この機能は、 末尾以外の場所での名前付き引数C#4.0で追加された名前付き引数が末尾以外でも利用できるようになった。 Hogehoge(name: "John", 17);
private protected アクセス修飾子同一アセンブリ内、かつ、継承先からのアクセス許可を表す 数値リテラルの改善十六進リテラルの int bin = 0b_01_01;
int hex = 0x_AB_CD;
C# 7.3からの仕様C#7.3では以下の仕様が追加された[a 41]。
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;
}
}
}
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)) { }
// C#7.2までは無効な指定(コンパイル自体は可能。無視される)
// C#7.3からはバッキングフィールドに対するAttribute指定と見なされる
[field: NonSerialized]
public int MyProperty { get; set; }
C# 8.0からの仕様C# 8.0で追加された仕様は以下の通り。[a 42][18] null許容参照型参照型にnull許容性を指定できるようになった。参照型の型名に 参照型の型名に フロー解析レベルでのnull許容性チェックが行われる。null許容値型の null許容コンテキスト参照型のnull許容性は、null許容コンテキストによって有効、無効の切り替えが可能である。 C#7.3以前の互換性のために、既定では無効となっている。
null免除演算子null許容参照型の変数名の後に インタフェースの既定メンバーインタフェースのメンバーに既定の実装を指定できるようになった。また、インタフェースに静的メンバーを持つことができるようになった。 さらに、インタフェースのメンバーにアクセシビリティを指定できるようになった。
パターンマッチングの拡張 (C# 8.0)
非同期ストリーム
async IAsyncEnumerable<int> EnumerateAsync() {
await Task.Delay(100);
yield return 1;
await Task.Delay(100);
yield return 2;
}
async void SpendAsync() {
await foreach (var item in EnumerateAsync()) {
Console.WriteLine(item);
}
}
範囲指定
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)
その他の仕様
C# 9.0からの仕様C# 9.0で追加された仕様は以下の通り。
C# 10.0からの仕様C# 10.0で追加された仕様は以下の通り。
C# 11.0からの仕様C# 11.0で追加された仕様は以下の通り[a 14][b 6]。 生文字列リテラルエスケープなどの加工を施さない文字列を3個の二重引用符で括って表現できる様になった。未加工の文字リテラルとも呼ばれる。 string logMsg = """
原因不明のエラーが発生しました。
詳細はログファイル "C:\Logs\exception.log" を確認してください。
""";
汎用属性属性の型が型引数を持てる様になった。 // 属性
[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 45][b 11]。 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 から始まりません。");
}
また、 bool CheckSignature(ReadOnlySpan<char> sig)
=> sig is "HOGE";
ジェネリック型数値演算型引数に「数値型または数値型に類似している型」である事を示す制約を付け加える機能が導入された[a 46][b 12]。また、それに呼応して下記の変更が行われた。
ジェネリック型数値演算を用いた一例を下記に示す[a 49][a 50]。 // 大抵の演算子インターフェイスは 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
その他の仕様
C# 12.0からの仕様C# 12.0で追加された仕様は以下の通り[a 17][b 7]。 クラスと構造体のプライマリコンストラクターレコード型( class Example(string message)
{
public string Message { get; } = message;
}
コレクション式配列、コレクション、 // 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];
その他の仕様
C# 13.0からの仕様C# 13.0で追加された仕様は以下の通り[a 21][b 8]。 新しいエスケープシーケンスエスケープ文字[注釈 8]を表すエスケープシーケンスとして 暗黙的なインデックスアクセスオブジェクト初期化子においても
|
Portal di Ensiklopedia Dunia