C Sharpのデータ型この記事では、プログラミング言語C#のデータ型について説明する。 それぞれのデータ型が取り込まれたバージョンについては、親記事C#を参照のこと。C#のデータ型は共通言語基盤 (CLI) のデータ型と密接な関係があり、ターゲットとする.NET Frameworkあるいは.NET Coreのバージョンによっては使用できないものも存在する。 参照型と値型C#の任意の型は (後述するポインタ型を除いて) 参照型と値型に二分される。 参照型の変数が、実際のオブジェクトを格納する領域の参照のみを保持するのに対して、 値型の変数は、実際のオブジェクトの値全てを保持する。 この特徴により、値の代入や引数の受け渡しの挙動が大きく異なって見える。 →「引数 § 参照の値渡し」も参照
参照型値型参照型にも値型にも分別されない型ポインタ型はobject型と相互に変換することができず、参照型にも値型にも相当しない[1]。 「任意の型について」記載している内容は、ポインタ型が当てはまらないことも多いことに留意されたい。例えば、#ジェネリクスの型引数にポインタ型を指定することはできない。 基本的なデータ型C#において、キーワード指定されたデータ型(組み込み型、built-in types)を下に示す。
decimalC#の
主なデータ型クラス型クラス型は、関連するメンバーをカプセル化した参照型である。メンバーにはフィールド、メソッド、プロパティ、コンストラクタなどが含まれる[2]。
構造体型構造体型は、関連するメンバーをカプセル化した値型である。
インターフェイス型インターフェイス型は、型の多重継承を実現するための抽象型である。
列挙型列挙型は、名前付き定数の集まりで構成される値型である。
/// <summary>光の三原色を1bitずつで表す</summary>
[Flags]
enum RgbColor : byte {
Black = 0,
Red = 1 << 0,
Green = 1 << 1,
Blue = 1 << 2,
Cyan = Green | Blue,
Magenta = Blue | Red,
Yellow = Red | Green,
White = Red | Green | Blue,
}
static class RgbColorExtension {
/// <summary>White (0b111) とのXORを返す拡張メソッド。C# 3.0以降が必要。</summary>
public static RgbColor GetComplementaryColor(this RgbColor color) { return color ^ RgbColor.White; }
}
static void Main() {
// Magenta を定義していない場合、"Red, Blue" が表示される。
Console.WriteLine(RgbColor.Green.GetComplementaryColor());
// Yellow を定義していない場合、"Red, Green" が表示される。
Console.WriteLine(RgbColor.Blue.GetComplementaryColor());
}
デリゲート型デリゲート型は、オブジェクトインスタンスへの参照とメソッドへの参照をまとめてカプセル化する参照型である。
配列型配列型は、初期化時に指定した型と長さを持つ参照型である。 ジャグ配列 (配列の配列) のほかに、真の多次元配列がサポートされる。 匿名型匿名型は一時的に使用される型を簡単に定義するためのクラス型。読み取り専用プロパティのみを持つ。
特定の型名を持たないが、 dynamic型dynamic型は、動的型付け変数を表す型である。 コンパイル時の型チェックをバイパスし、実行時に演算が解決される。 ポインタ型ポインタ型はC++ライクなポインタ操作を可能とする型である。
通常の共通型システムの型とは異なり、共通言語ランタイムでは検査されない。
ジェネリクスC#はジェネリックプログラミングに対応する。 クラス、構造体、インターフェイス、デリゲート、メソッドに対して、型引数を適用することができる。 型引数は /// <summary>型引数を取るinterfaceの例</summary>
/// <typeparam name="TIn">反変な型引数</typeparam>
/// <typeparam name="TOut">共変な型引数</typeparam>
/// <typeparam name="T">不変な型引数</typeparam>
interface ISampleTypeParameter<in TIn, out TOut, T> {
void Procedure(TIn input);
TOut Supply();
T Function(T input);
void UseDelegate(Func<TOut, TIn> func);
void UseDelegate(Func<T, T> func);
Func<TIn, TOut> SupplyDelegate1();
Func<T, T> SupplyDelegate2();
}
特別扱いされる型C#において、文法上特別扱いを受ける型の例を下に示す。 Exception型
Attribute型
IEnumerable<T>, IEnumerator<T>
IDisposable
Nullable<T>型名の後ろに int? maybeNum = null;
int correctlyNum = maybeNum ?? 0;
上記構文は下記の糖衣構文である。 Nullable<int> maybeNum = null;
int correctlyNum = maybeNum.HasValue ? maybeNum.Value : 0;
また、Null許容型では、一部を除いて各演算子が再定義される。 // Null許容int型の演算子再定義
int? x = null;
int? y = 123;
// nullを含む単項演算子、二項演算子 (NULL,NULL,NULL,NULL)
Console.WriteLine((x + y)?.ToString() ?? "NULL");
Console.WriteLine((x << y)?.ToString() ?? "NULL");
Console.WriteLine((-x)?.ToString() ?? "NULL");
Console.WriteLine((++x)?.ToString() ?? "NULL");
// 片方がnullの場合の比較演算子(false,false,false,true)
Console.WriteLine($"{x <= y},{x == y},{x >= y},{x != y}");
// 双方がnullの場合の比較演算子(false,true,false,false)
int? z = null;
Console.WriteLine($"{x <= z},{x == z},{x >= z},{x != z}");
// Null許容bool型の演算子再定義
bool? b1 = null;
bool? b2 = true;
// nullを含む単項演算子、二項演算子 (NULL,NULL)
Console.WriteLine((b1 & b2)?.ToString() ?? "NULL");
Console.WriteLine((!b1)?.ToString() ?? "NULL");
// ショートサーキットは再定義されない
//Console.WriteLine((b1 && b2)?.ToString() ?? "NULL");
// Null許容bool型のままでは条件式では使用できない
//if (b1) { DoSomething(); }
if (b1 ?? false) { DoSomething(); }
if (b1 == true) { DoSomething(); }
Task, Task<T>, ValueTask<T>async/awaitキーワードによる、非同期プログラミングに対応する。 ValueTuple<>2個以上の要素を持つValueTupleについては、例えば IFormattable, FormattableString挿入文字列リテラルから暗黙の型変換が可能である。 // 書式指定可能(IFormattable)型で受け取ることができる。
IFormattable fmt = $"id: {id:D}, money: {money:C}";
var str = fmt.ToString(null, new System.Globalization.CultureInfo("ja-JP"));
Expression<T>式形式のラムダから暗黙の型変換が可能である。これによって、式木をシンプルに記述できる。 // 本体が式のラムダ式をExpression型に格納することができる。
System.Linq.Expressions.Expression<Func<int, int>> expression = x => x * x;
// 本体がステートメントのラムダ式はExpressionとして扱えない。
//System.Linq.Expressions.Expression<Func<int, int>> expression = x => { return x * x; };
Span<T>
// C# 7.2から可能
Span<int> span1 = stackalloc int[4];
// C# 7.3から初期化子にも対応
Span<int> span2 = stackalloc[] { 10, 20, 30, 40 };
特別扱いされるメソッドC#において、ダック・タイピング的な扱いを受ける例を下に示す。 特定のメソッドが定義されていれば、クラス間の継承関係に関わらずC#の文法の恩恵を受けることができる。 Add()
/// <summary>IEnumerableを実装、かつ、Addメソッドを持つ</summary>
class MultiplyList : IEnumerable {
List<int> _list = new List<int>();
public void Add(int x) => _list.Add(x);
public void Add(int x, int y) => _list.Add(x * y);
public void Add(int x, int y, int z) => _list.Add(x * y * z);
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}
// Add()のオーバーロードがそれぞれ呼び出される。
var list = new MultiplyList() {
{ 1 },
{ 2, 3 },
{ 4, 5, 6 },
};
GetEnumerator()
/// <summary>GetEnumeratorメソッドを実装する型</summary>
class DaysInMonths {
public IEnumerator<int> GetEnumerator() {
yield return 31; yield return 28; yield return 31;
yield return 30; yield return 31; yield return 30;
yield return 31; yield return 31; yield return 30;
yield return 31; yield return 30; yield return 31;
}
}
var months = new DaysInMonths();
// foreach構文
foreach (var days in months) {
Console.WriteLine(days);
}
Deconstruct()
/// <summary>Deconstructメソッドを実装する型</summary>
class Person {
public string Name { get; set; }
public int Age { get; set; }
/// <summary>分解メソッド</summary>
public void Deconstruct(out string name, out int age) {
name = Name;
age = Age;
}
}
var person = new Person() { Name = "Taro", Age = 18 };
// 分解構文
var (name, age) = person;
// 以下のDeconstructメソッド呼び出しと同じ
person.Deconstruct(out var name, out var age);
GetAwaiter()
GetPinnableReference()
型情報
この型情報からリフレクションによるプログラミングに対応する。 次のコードは // コンパイル時型情報
Type typeAtCompiling = typeof(int); /* System.Int32 */
// 実行時型情報
object obj = "Hello!";
Type typeAtExecuting = obj.GetType(); /* System.String */
// 指定した名前の型情報を取得
Type typeByNominate = Type.GetType("System.EventHandler");
CheckType(typeAtCompiling);
CheckType(typeAtExecuting);
CheckType(typeByNominate);
static void CheckType(Type type) {
Console.WriteLine(type);
Console.WriteLine("* 参照型: " + type.IsClass);
Console.WriteLine("** インターフェイス型: " + type.IsInterface);
Console.WriteLine("** 配列型: " + type.IsArray);
Console.WriteLine("** デリゲート型: " + type.IsSubclassOf(typeof(MulticastDelegate)));
Console.WriteLine("* 値型: " + type.IsValueType);
Console.WriteLine("** 列挙型: " + type.IsEnum);
Console.WriteLine("** プリミティブ: " + type.IsPrimitive);
Console.WriteLine("* 総称型: " + type.IsGenericType);
Console.WriteLine("* ポインタ型: " + type.IsPointer);
}
脚注
関連項目 |
Portal di Ensiklopedia Dunia