C#の種類を初心者向けに解説!データ型・変数・クラスの違いと使い分け一覧

はじめに

C#を学び始めると、「intやstringは何の種類なのか」「変数とデータ型は何が違うのか」「classやstructはどのように使い分けるのか」といった疑問が生まれます。

C#には、数値や文字列を表すデータ型、値を保存する変数、複数のデータや処理をまとめるクラスなど、さまざまな種類があります。これらは互いに関係していますが、役割は同じではありません。

この記事では、C#の種類を次の観点から初心者向けに解説します。

  • データ型の種類

  • 変数の種類

  • class・struct・recordなどの違い

  • コレクション型の使い分け

  • 型変換の方法

  • 用途に応じた型の選び方

コード例を交えながら、C#の型・変数・クラスの違いを順番に整理していきましょう。

1. C#の「種類」とは?初心者が押さえるべき3つの分類

C#の「種類」について調べると、データ型、変数、クラスなど、異なる概念が一緒に紹介されていることがあります。

最初に、次の3つへ分けて考えると理解しやすくなります。

  1. データ型の種類

  2. 変数の種類

  3. クラスなどのユーザー定義型の種類

1-1. データ型の種類

データ型とは、プログラムで扱う値の種類を表すものです。

たとえば、次のような型があります。

int age = 20;double height = 170.5;bool isStudent = true;char grade = 'A';string name = "田中";

それぞれのデータ型には、保存できる値や利用できる処理が決められています。

データ型主な用途値の例
int整数10
double小数3.14
bool真偽値true
char1つの文字'A'
string文字列"Hello"

C#のデータ型は、大きく値型参照型に分類できます。

1-2. 変数の種類

変数とは、値を保存しておくための名前付きの領域です。

int score = 80;

このコードでは、次のように役割が分かれています。

  • int:データ型

  • score:変数名

  • 80:変数へ代入する値

変数は、宣言する場所や目的によって、ローカル変数、インスタンス変数、静的変数などに分類されます。

class Player{private int score;             // インスタンス変数private static int playerCount; // 静的変数
public void Play(){int bonus = 10;            // ローカル変数}

}

同じint型でも、どこに宣言するかによって変数の種類や有効範囲が変わります。

1-3. クラスなどのユーザー定義型の種類

C#では、最初から用意されているintstringだけでなく、自分で新しい型を作れます。

代表的なユーザー定義型は次のとおりです。

  • class

  • struct

  • record

  • interface

  • enum

  • delegate

たとえば、商品を表すProductクラスは次のように定義できます。

class Product{public string Name { get; set; } = "";public decimal Price { get; set; }}

定義したProductは、intstringと同じように変数の型として使用できます。

Product product = new Product{Name = "キーボード",Price = 5000m};

1-4. データ型・変数・クラスの違いを一覧で比較

用語役割
データ型値の種類や扱い方を決めるintstring
変数値や参照を保存するagename
クラス独自の参照型を定義するProductUser
オブジェクトクラスなどを基に作られた実体new Product()
インスタンスクラスを基に生成された具体的なオブジェクトproductが参照する実体

「クラスは設計図、オブジェクトは設計図から作った実体、変数はその実体を扱うための名前」と考えると理解しやすいでしょう。

2. C#のデータ型は「値型」と「参照型」に分けられる

C#のデータ型を理解するうえで、特に重要なのが値型と参照型の違いです。

両者では、変数への代入やメソッドへの受け渡しを行ったときの動作が異なります。

2-1. 値型とは

値型は、変数が値そのものを保持する型です。

主な値型には次のものがあります。

  • 整数型

  • 小数型

  • bool

  • char

  • struct

  • enum

  • タプルなどに使われるValueTuple

int number = 10;

numberには整数値の10が保存されています。

値型の変数を別の変数へ代入すると、原則として値がコピーされます。

int a = 10;int b = a;

b = 20;

Console.WriteLine(a); // 10Console.WriteLine(b); // 20

bを変更しても、コピー元のaには影響しません。

2-2. 参照型とは

参照型は、オブジェクトそのものではなく、オブジェクトを参照するための情報を変数が保持する型です。

主な参照型には次のものがあります。

  • string

  • object

  • 配列

  • class

  • interface

  • delegate

  • 標準のrecord

class User{public string Name { get; set; } = "";}

User user = new User();

user変数には、生成されたUserオブジェクトへの参照が保存されます。

参照型の変数を別の変数へ代入すると、通常は同じオブジェクトを指す参照がコピーされます。

2-3. 値型と参照型の違い

比較項目値型参照型
変数が保持するもの値そのものオブジェクトへの参照
代入時の基本動作値をコピー参照をコピー
代表例intdoubleboolstructstring、配列、class
通常の初期値0falseなどnull
nullの代入通常は不可設定によって可能
継承構造体は別のクラスを継承できないクラスは継承できる

実際のメモリ配置や最適化は実行環境によって異なるため、「値型は必ずスタック、参照型は必ずヒープ」と単純に決めつけないことも重要です。

初心者の段階では、まず「代入時に値がコピーされるか、参照がコピーされるか」という違いを理解するとよいでしょう。

2-4. 値のコピーと参照のコピー

値型では、変数ごとに独立した値が扱われます。

int original = 100;int copied = original;

copied = 200;

Console.WriteLine(original); // 100

参照型では、2つの変数が同じオブジェクトを参照する場合があります。

class User{public string Name { get; set; } = "";}

User user1 = new User { Name = "佐藤" };User user2 = user1;

user2.Name = "鈴木";

Console.WriteLine(user1.Name); // 鈴木

user1user2は、同じUserオブジェクトを参照しています。そのため、user2を通して名前を変更すると、user1から見える名前も変わります。

別のオブジェクトとして複製したい場合は、新しいインスタンスを生成して値を移す必要があります。

User user2 = new User{Name = user1.Name};

2-5. nullを代入できる型・できない型

nullは、参照先や値が存在しない状態を表します。

通常の値型には、そのままnullを代入できません。

int number = null; // コンパイルエラー

値型でnullを扱いたい場合は、Null許容値型を使います。

int? number = null;bool? answer = null;

参照型にはnullを代入できます。

string? name = null;User? user = null;

Null許容参照型を有効にしているプロジェクトでは、stringstring?の意図が区別されます。

string requiredName = "田中";string? optionalName = null;

ただし、Null許容参照型は主にコンパイラの静的解析機能です。string?という別のCLR型が生成されるわけではありません。

3. C#でよく使う値型の種類

C#の値型には、数値、真偽値、文字、構造体、列挙型などがあります。

3-1. 整数型(byte・short・int・long)

整数型は、小数点を含まない数値を扱います。

ビット数おおよその範囲
byte80~255
short16-32,768~32,767
int32約-21億~21億
long64非常に大きな整数

一般的な整数にはintを使います。

int age = 25;int score = 100;

intの範囲を超える可能性がある場合はlongを使います。

long population = 8_000_000_000L;

数値の途中に記述したアンダースコアは、読みやすくするための区切りです。値には影響しません。

byteは画像データやバイナリデータなど、0~255の範囲を扱う場面で使用されます。

byte red = 255;

3-2. 符号なし整数型(ushort・uint・ulong)

符号なし整数型は、負の値を扱わず、0以上の整数だけを保存します。

ビット数最小値
ushort160
uint320
ulong640
uint count = 100U;ulong largeCount = 10_000_000_000UL;

負の値が不要であることを明確にできますが、一般的な.NETのAPIではintが広く使われています。特別な理由がなければ、整数にはintを選ぶほうが扱いやすいでしょう。

なお、8ビットの符号付き整数としてsbyteも用意されています。

3-3. 小数型(float・double・decimal)

C#で小数を扱う主な型は、floatdoubledecimalです。

特徴主な用途
float精度が比較的低く、サイズが小さい3D座標、ゲーム、画像処理
double一般的な浮動小数点数科学計算、計測値
decimal10進数を高精度で扱える金額、会計計算
float temperature = 36.5f;double distance = 123.456;decimal price = 1980.50m;

floatにはfdecimalにはmを付けます。小数リテラルは、何も付けなければ通常doubleとして扱われます。

金額には、2進浮動小数点数の誤差を避けやすいdecimalが適しています。

decimal unitPrice = 120.50m;decimal total = unitPrice * 3;

3-4. 真偽値型(bool)

boolは、真または偽の2つの状態を表す型です。

bool isActive = true;bool isDeleted = false;

条件分岐によく使用します。

if (isActive){Console.WriteLine("有効です");}

数値の01を、暗黙的にboolとして使用することはできません。

int value = 1;// if (value) { } // コンパイルエラー

比較式を使い、明示的に真偽値を作ります。

if (value == 1){Console.WriteLine("値は1です");}

3-5. 文字型(char)

charは、UTF-16のコード単位を1つ保持する値型です。

char initial = 'A';char symbol = 'あ';

charの値は、シングルクォーテーションで囲みます。

char grade = 'A';

文字列を表すstringは、ダブルクォーテーションで囲みます。

string gradeText = "A";

絵文字など、一部の文字は複数のcharによって表現されることがあります。そのため、見た目上の「1文字」とcharが常に一致するとは限りません。

3-6. 構造体型(struct)

structは、複数の値をまとめて独自の値型を定義する仕組みです。

public struct Point{public int X { get; set; }public int Y { get; set; }

public Point(int x, int y){X = x;Y = y;}

}

次のように使用します。

Point point = new Point(10, 20);

Console.WriteLine(point.X);Console.WriteLine(point.Y);

構造体は値型なので、代入時には値がコピーされます。

Point point1 = new Point(10, 20);Point point2 = point1;

point2.X = 100;

Console.WriteLine(point1.X); // 10

小さく、値として扱いたいデータに向いています。座標、日付、色、範囲などが代表例です。

3-7. 列挙型(enum)

enumは、関連する固定の選択肢に名前を付けて表す型です。

public enum OrderStatus{Pending,Paid,Shipped,Completed,Canceled}

次のように使用できます。

OrderStatus status = OrderStatus.Paid;

if (status == OrderStatus.Paid){Console.WriteLine("支払い済みです");}

文字列や数値を直接使うよりも、選択肢が分かりやすくなり、入力ミスも防ぎやすくなります。

内部的には整数値を持ち、特に指定しなければ最初のメンバーが0、次が1となります。

public enum Priority{Low = 1,Medium = 2,High = 3}

3-8. Null許容値型(int?・bool?など)

Null許容値型は、通常の値に加えてnullを保存できる値型です。

int? age = null;bool? answer = null;DateTime? completedAt = null;

T?は、値型では基本的にNullable<T>の省略記法です。

int? number1 = null;Nullable<int> number2 = null;

値が入っているかは、HasValueで確認できます。

int? number = 10;

if (number.HasValue){Console.WriteLine(number.Value);}

Null合体演算子を使うと、nullの場合の代替値を指定できます。

int? input = null;int result = input ?? 0;

データベースの未入力値や任意入力の項目を扱うときに便利です。

4. C#でよく使う参照型の種類

参照型には、文字列、配列、クラス、インターフェース、デリゲートなどがあります。

4-1. 文字列型(string)

stringは、複数の文字からなる文字列を扱う参照型です。

string name = "山田太郎";string message = "こんにちは";

文字列は+演算子で連結できます。

string firstName = "太郎";string lastName = "山田";

string fullName = lastName + firstName;

文字列補間を使うと、変数を読みやすく埋め込めます。

string fullName = $"{lastName} {firstName}";

stringは参照型ですが、内容を直接変更できない不変型です。

string text = "Hello";text = text + " C#";

この処理では、元の文字列が書き換えられるのではなく、新しい文字列が作られます。大量の文字列を繰り返し連結する場合は、StringBuilderの利用を検討します。

4-2. オブジェクト型(object)

objectは、C#におけるすべての型の基になる型です。

object value1 = 100;object value2 = "C#";object value3 = true;

さまざまな値を保存できますが、取り出して特定の型として使うには型の確認や変換が必要です。

object value = "Hello";

if (value is string text){Console.WriteLine(text.Length);}

値型をobjectへ代入すると、ボックス化が行われます。

int number = 10;object boxed = number;

objectから値型へ戻す処理は、ボックス化解除と呼ばれます。

int restored = (int)boxed;

柔軟性は高いものの、型安全性や性能の面では具体的な型を使うほうが適切な場合が多くあります。

4-3. 配列型

配列は、同じ型の複数の要素をまとめて扱う参照型です。

int[] scores = { 80, 90, 75 };string[] names = { "佐藤", "鈴木", "高橋" };

添字は0から始まります。

Console.WriteLine(scores[0]); // 80

要素数はLengthで取得できます。

Console.WriteLine(scores.Length); // 3

配列は生成時に要素数が決まり、後から長さを直接変更できません。

int[] numbers = new int[5];

要素数を頻繁に増減させたい場合は、List<T>のほうが適しています。

4-4. クラス型(class)

classは、関連するデータと処理をまとめた独自の参照型を定義する仕組みです。

public class BankAccount{public string Owner { get; set; } = "";public decimal Balance { get; private set; }

public void Deposit(decimal amount){Balance += amount;}

}

使用例は次のとおりです。

BankAccount account = new BankAccount{Owner = "田中"};

account.Deposit(1000m);

Console.WriteLine(account.Balance);

クラスには、フィールド、プロパティ、メソッド、コンストラクターなどを定義できます。

4-5. インターフェース型(interface)

interfaceは、クラスや構造体が実装すべき機能の契約を定義します。

public interface IPrintable{void Print();}

クラス側でインターフェースを実装します。

public class Report : IPrintable{public void Print(){Console.WriteLine("レポートを印刷します");}}

インターフェース型の変数には、そのインターフェースを実装したオブジェクトを代入できます。

IPrintable printable = new Report();printable.Print();

実装クラスが異なっていても、共通の操作として扱えるのが利点です。

4-6. デリゲート型(delegate)

デリゲートは、メソッドを参照して変数のように扱うための型です。

public delegate int Calculate(int x, int y);

定義したデリゲートには、引数と戻り値の形式が一致するメソッドを代入できます。

static int Add(int x, int y){return x + y;}

Calculate calculate = Add;int result = calculate(3, 5);

実際の開発では、標準のActionFuncがよく使われます。

Action<string> showMessage = message => Console.WriteLine(message);Func<int, int, int> multiply = (x, y) => x * y;

デリゲートは、イベント処理、コールバック、LINQなどで広く利用されています。

4-7. レコード型(record)

recordは、主にデータを表現するための型です。標準のrecordは参照型であり、値に基づく等価比較が標準で用意されています。

public record User(string Name, int Age);

使用例は次のとおりです。

User user1 = new User("佐藤", 20);User user2 = new User("佐藤", 20);

Console.WriteLine(user1 == user2); // True

通常のクラスでは、既定の比較は同じ参照かどうかを基準にします。一方、レコードでは各メンバーの値が同じかどうかを基準に比較できます。

with式を使うと、一部の値を変更したコピーを作れます。

User user3 = user1 with { Age = 21 };

なお、値型のレコードを作るrecord structもあります。

public readonly record struct Point(int X, int Y);

4-8. dynamic型

dynamicを使うと、コンパイル時の型チェックの一部を実行時まで遅らせられます。

dynamic value = "Hello";Console.WriteLine(value.Length);

value = 100;Console.WriteLine(value + 50);

dynamicで宣言した変数の実体は、実行時には具体的な型の値です。dynamic専用のCLR型が存在するわけではなく、コンパイラが動的に処理します。

存在しないメンバーを呼び出しても、コンパイル時にはエラーにならない場合があります。

dynamic value = 100;value.NotExists(); // 実行時エラー

柔軟性は高いものの、実行時エラーが発生しやすく、コード補完やリファクタリングの支援も弱くなります。COM連携や動的データの処理など、必要な場面に限定して使用するのが基本です。

5. C#の変数の種類と宣言方法

C#の変数は、宣言する場所や共有範囲、値を変更できるかどうかによって分類されます。

5-1. ローカル変数

ローカル変数は、メソッドやブロックの内部で宣言する変数です。

static void ShowTotal(){int price = 1000;int quantity = 3;int total = price * quantity;

Console.WriteLine(total);

}

ローカル変数は、宣言されたブロック内だけで使用できます。

if (true){string message = "表示します";Console.WriteLine(message);}

// Console.WriteLine(message); // スコープ外なのでエラー

ローカル変数は、原則として値を代入してから使用する必要があります。

5-2. インスタンス変数(フィールド)

インスタンス変数は、クラスのインスタンスごとに保持されるフィールドです。

public class Counter{private int count;

public void Increment(){count++;}public int GetCount(){return count;}

}

それぞれのインスタンスが、独立したcountを持ちます。

Counter counter1 = new Counter();Counter counter2 = new Counter();

counter1.Increment();

Console.WriteLine(counter1.GetCount()); // 1Console.WriteLine(counter2.GetCount()); // 0

外部から直接変更させる必要がなければ、フィールドはprivateにするのが一般的です。

5-3. 静的変数(staticフィールド)

staticフィールドは、インスタンスごとではなく、型全体で1つだけ共有されます。

public class User{public static int Count;

public User(){Count++;}

}

複数のインスタンスを生成しても、同じCountが共有されます。

User user1 = new User();User user2 = new User();

Console.WriteLine(User.Count); // 2

staticメンバーには、通常、インスタンス変数ではなくクラス名を使ってアクセスします。

グローバルな状態を持たせすぎると、処理の依存関係が分かりにくくなるため、必要性を検討して使用しましょう。

5-4. 引数と戻り値

引数は、メソッドへ値を渡すための変数です。戻り値は、メソッドから呼び出し元へ返す値です。

static int Add(int x, int y){return x + y;}

この例では、xyが引数、intが戻り値の型です。

int result = Add(3, 5);

戻り値がないメソッドではvoidを指定します。

static void ShowMessage(string message){Console.WriteLine(message);}

引数に既定値を設定すると、省略可能な引数にできます。

static void Greet(string name = "ゲスト"){Console.WriteLine($"こんにちは、{name}さん");}

5-5. 定数(const)

constは、コンパイル時に値が確定し、後から変更できない定数を宣言するときに使います。

const int MaxUsers = 100;const string AppName = "SampleApp";

定数へ再代入することはできません。

// MaxUsers = 200; // コンパイルエラー

クラス内にも定義できます。

public class Tax{public const decimal Rate = 0.1m;}

constは暗黙的に静的なメンバーとして扱われるため、インスタンスを作らずにアクセスできます。

decimal taxRate = Tax.Rate;

5-6. 読み取り専用変数(readonly)

readonlyフィールドは、宣言時またはコンストラクター内でのみ値を設定できます。

public class User{private readonly DateTime createdAt;

public User(){createdAt = DateTime.Now;}

}

constと異なり、実行時に決まる値も設定できます。

public class Application{public readonly string StartTime;

public Application(){StartTime = DateTime.Now.ToString();}

}

主な違いは次のとおりです。

項目constreadonly
値が決まる時点コンパイル時実行時でも可
設定できる場所宣言時宣言時またはコンストラクター
基本的な用途完全に固定された定数インスタンス生成後に変えない値
静的か暗黙的に静的通常はインスタンスごと

5-7. 暗黙的に型を決めるvar

varを使うと、右辺の値からコンパイラが変数の型を推論します。

var age = 20;          // intvar name = "田中";     // stringvar price = 1000.5m;   // decimal

varは「どの型でも入る変数」ではありません。宣言時に型が決まり、その後は別の型を代入できません。

var number = 10;// number = "10"; // コンパイルエラー

型を推論するため、宣言時の初期値が必要です。

// var value;       // エラー// var value = null; // 型を判断できないためエラー

右辺から型が明らかな場合や、LINQの匿名型を扱う場合に便利です。

var user = new User();

一方、右辺を見ても型が分かりにくい場合は、明示的な型名を書いたほうが読みやすくなります。

5-8. 変数のスコープと有効範囲

スコープとは、変数を使用できる範囲です。

static void Sample(){int outer = 10;

if (outer &gt; 0){int inner = 20;Console.WriteLine(outer);Console.WriteLine(inner);}Console.WriteLine(outer);// Console.WriteLine(inner); // スコープ外

}

一般的なスコープは次のとおりです。

変数の種類主な有効範囲
ローカル変数宣言されたブロック内
引数メソッド内
インスタンスフィールド対象インスタンス内
staticフィールド型全体
const宣言された型やブロック内

変数は、必要な範囲のできるだけ内側で宣言するのが基本です。スコープを小さくすると、値がどこで使われているか分かりやすくなります。

6. C#のクラスとは?データ型・変数との違い

クラスは、関連するデータと処理をひとまとまりにするための仕組みです。

6-1. クラスは独自のデータ型を作る仕組み

C#のクラスを定義することは、新しい参照型を作ることを意味します。

public class Product{public string Name { get; set; } = "";public decimal Price { get; set; }}

これにより、Productを型名として利用できます。

Product product;

Productは型であり、productは変数です。

6-2. クラスとオブジェクト・インスタンスの違い

クラス、オブジェクト、インスタンスは、次のように区別できます。

用語意味
クラスオブジェクトの構造や動作を定義した型
オブジェクトプログラム上で扱われるデータの実体
インスタンスクラスを基に生成された具体的なオブジェクト
変数インスタンスへの参照を保持する名前
Product product = new Product();

このコードでは、Productがクラス、new Product()で生成されるものがインスタンス、productが参照を保持する変数です。

6-3. クラスに定義できるフィールド

フィールドは、クラス内部でデータを保持する変数です。

public class Player{private int score;}

フィールドは、外部へ直接公開せず、privateにするのが一般的です。

public class Player{private int score;

public void AddScore(int point){if (point &gt; 0){score += point;}}

}

処理をメソッド経由にすると、不正な値が設定されるのを防ぎやすくなります。

6-4. クラスに定義できるプロパティ

プロパティは、フィールドの値へ安全にアクセスするための仕組みです。

public class User{public string Name { get; set; } = "";}

値の取得だけを許可することもできます。

public class User{public int Id { get; }

public User(int id){Id = id;}

}

外部からの変更を制限しつつ、クラス内部では変更できるプロパティも定義できます。

public decimal Balance { get; private set; }

値の検証を追加することも可能です。

private int age;

public int Age{get => age;set{if (value < 0){throw new ArgumentOutOfRangeException(nameof(value));}

    age = value;}

}

6-5. クラスに定義できるメソッド

メソッドは、クラスが行う処理や動作を定義します。

public class Calculator{public int Add(int x, int y){return x + y;}}

次のように呼び出します。

Calculator calculator = new Calculator();int result = calculator.Add(3, 5);

クラスが持つデータを操作するメソッドも定義できます。

public class BankAccount{public decimal Balance { get; private set; }

public void Deposit(decimal amount){if (amount &lt;= 0){throw new ArgumentException("金額は0より大きくしてください");}Balance += amount;}

}

6-6. コンストラクターの役割

コンストラクターは、インスタンス生成時に呼び出される特別な処理です。

public class Product{public string Name { get; }public decimal Price { get; }

public Product(string name, decimal price){Name = name;Price = price;}

}

インスタンスを生成するときに必要な値を渡します。

Product product = new Product("マウス", 3000m);

必須項目をコンストラクターの引数にすると、不完全な状態のインスタンスが作られるのを防ぎやすくなります。

6-7. クラスを定義して変数に代入する例

商品情報を管理するクラスを作成してみましょう。

public class Product{public string Name { get; }public decimal Price { get; private set; }

public Product(string name, decimal price){Name = name;ChangePrice(price);}public void ChangePrice(decimal newPrice){if (newPrice &lt; 0){throw new ArgumentOutOfRangeException(nameof(newPrice));}Price = newPrice;}public void ShowInfo(){Console.WriteLine($"{Name}: {Price:N0}円");}

}

作成したクラスを型として変数を宣言します。

Product product = new Product("キーボード", 5000m);

product.ShowInfo();product.ChangePrice(4500m);product.ShowInfo();

クラスを使うと、商品名や価格などのデータと、価格変更や情報表示などの処理を1か所へまとめられます。

7. class・struct・record・interface・enumの違い

C#には、独自の型や契約を定義する複数の仕組みがあります。それぞれの特徴を理解して使い分けましょう。

7-1. classを使うケース

classは、状態と処理を持つオブジェクトを表現するときに適しています。

public class Customer{public string Name { get; set; } = "";public List<Order> Orders { get; } = new();

public void AddOrder(Order order){Orders.Add(order);}

}

次のようなケースで使われます。

  • ユーザーや商品など、識別される対象を表す

  • 状態が変化するオブジェクトを作る

  • 継承を利用する

  • 複数の場所から同じインスタンスを共有する

  • データと処理をまとめる

迷った場合、一般的な業務オブジェクトにはclassを選ぶことが多いでしょう。

7-2. structを使うケース

structは、小さくて単純な値を表す場合に適しています。

public readonly struct Temperature{public double Celsius { get; }

public Temperature(double celsius){Celsius = celsius;}

}

次のようなケースが代表的です。

  • 座標や範囲など、値として扱いたい

  • サイズが比較的小さい

  • 不変なデータとして設計できる

  • nullではなく既定値を持たせたい

  • 値のコピーが自然な意味を持つ

大きな構造体は、コピーの負担や意図しない変更によって扱いにくくなる場合があります。基本的には小さく、不変な設計が適しています。

7-3. recordを使うケース

recordは、値の組み合わせによって意味が決まるデータを表現する場合に向いています。

public record Address(string PostalCode,string Prefecture,string City,string Street);

次のようなケースで便利です。

  • DTOやAPIの入出力モデル

  • 設定情報

  • イベントデータ

  • 値による等価比較を行いたいデータ

  • 不変に近いデータを扱いたい場合

with式によるコピーも簡潔です。

Address newAddress = oldAddress with{City = "横浜市"};

識別子とライフサイクルを持つ対象にはclass、値の組み合わせが重要なデータにはrecordという分け方が一つの目安です。

7-4. interfaceを使うケース

interfaceは、異なる型へ共通の操作を持たせたい場合に使います。

public interface IPaymentService{void Pay(decimal amount);}

複数の実装を作成できます。

public class CreditCardPayment : IPaymentService{public void Pay(decimal amount){Console.WriteLine($"クレジットカードで{amount:N0}円を支払います");}}

public class CashPayment : IPaymentService{public void Pay(decimal amount){Console.WriteLine($"現金で{amount:N0}円を支払います");}}

呼び出し側は具体的な実装ではなく、インターフェースに依存できます。

static void Checkout(IPaymentService paymentService, decimal amount){paymentService.Pay(amount);}

実装の切り替えやテストがしやすくなるのが大きな利点です。

7-5. enumを使うケース

enumは、選択肢が固定されている状態や区分を表す場合に使います。

public enum PaymentMethod{Cash,CreditCard,BankTransfer}

次のような用途に適しています。

  • 注文状態

  • 曜日

  • 権限区分

  • 支払い方法

  • 優先度

選択肢が頻繁に増減し、データベース上で動的に管理される項目には、enumよりもマスターデータやクラスのほうが適している場合があります。

7-6. 5種類の特徴と使い分け一覧

種類値型・参照型主な目的適した用途
class参照型状態と処理を持つ型を定義ユーザー、商品、サービス
struct値型小さな値を表現座標、範囲、独自の数値
record標準では参照型データ中心の型を定義DTO、設定値、イベント
interface参照型として扱われる契約共通操作を定義実装の差し替え、疎結合化
enum値型固定された選択肢を表現状態、区分、優先度

判断に迷った場合は、次の基準が役立ちます。

  • 状態や振る舞いを持つ対象:class

  • 小さな値:struct

  • データの値そのものが重要:record

  • 実装すべき機能の契約:interface

  • 固定された選択肢:enum

8. コレクション型の種類と使い分け

コレクション型は、複数の値をまとめて管理するための型です。データの追加、検索、重複の扱い、取り出す順番などによって適切な種類が異なります。

8-1. 配列(T[])

配列は、同じ型の要素を固定長で管理します。

int[] numbers = { 10, 20, 30 };

添字を使って高速にアクセスできます。

Console.WriteLine(numbers[1]); // 20

要素を順番に処理する場合は、foreachを使用できます。

foreach (int number in numbers){Console.WriteLine(number);}

要素数が最初から決まっている場合や、固定長のデータを扱う場合に適しています。

8-2. List

List<T>は、要素数を動的に変更できるコレクションです。

List<string> names = new List<string>();

names.Add("佐藤");names.Add("鈴木");names.Add("高橋");

コレクション式や簡潔な初期化構文を使える環境では、より短く記述できます。

List<string> names = ["佐藤", "鈴木", "高橋"];

要素の削除も可能です。

names.Remove("鈴木");

一般的な「複数件のデータ」には、List<T>がよく使われます。

8-3. Dictionary<TKey, TValue>

Dictionary<TKey, TValue>は、キーと値を組み合わせて管理します。

Dictionary<string, int> scores = new Dictionary<string, int>{["佐藤"] = 80,["鈴木"] = 90};

キーを使って値を取得できます。

Console.WriteLine(scores["佐藤"]);

存在しないキーへ直接アクセスすると例外が発生するため、TryGetValueを使うと安全です。

if (scores.TryGetValue("高橋", out int score)){Console.WriteLine(score);}else{Console.WriteLine("データがありません");}

IDからユーザーを探す場合や、商品コードと商品情報を対応付ける場合に適しています。

8-4. HashSet

HashSet<T>は、重複しない要素を管理するコレクションです。

HashSet<string> tags = new HashSet<string>();

tags.Add("C#");tags.Add(".NET");tags.Add("C#");

同じ値を追加しても、重複して保存されません。

Console.WriteLine(tags.Count); // 2

値が含まれているかを効率よく確認できます。

if (tags.Contains("C#")){Console.WriteLine("C#が含まれています");}

重複排除や集合演算を行いたい場合に適しています。

8-5. QueueとStack

Queue<T>は、先に追加した要素から取り出す先入れ先出しのコレクションです。

Queue<string> tasks = new Queue<string>();

tasks.Enqueue("タスク1");tasks.Enqueue("タスク2");

string firstTask = tasks.Dequeue();Console.WriteLine(firstTask); // タスク1

処理待ちのデータやメッセージを順番に扱う場合に向いています。

Stack<T>は、後に追加した要素から取り出す後入れ先出しのコレクションです。

Stack<string> history = new Stack<string>();

history.Push("画面1");history.Push("画面2");

string current = history.Pop();Console.WriteLine(current); // 画面2

操作履歴、戻る処理、数式解析などに利用されます。

8-6. 配列とListの違い

比較項目配列List<T>
要素数固定動的に変更可能
要素の追加基本的に不可Addで追加可能
要素の削除基本的に不可Removeなどで削除可能
添字アクセス可能可能
主な用途固定長データ件数が変わるデータ
宣言例int[]List<int>

要素数が確定している場合は配列、増減する可能性がある場合はList<T>を使うのが基本です。

8-7. 用途別に選ぶコレクション型一覧

用途適したコレクション
固定件数のデータ配列
順番付きで要素を増減したいList<T>
キーから値を探したいDictionary<TKey, TValue>
重複を排除したいHashSet<T>
追加順に処理したいQueue<T>
最後に追加したものから処理したいStack<T>

初心者が最初に覚えるなら、配列とList<T>から始め、キー検索が必要になったらDictionary<TKey, TValue>を学ぶとよいでしょう。

9. C#の型変換の種類

型変換とは、ある型の値を別の型として扱えるようにする処理です。

変換方法によって、情報が失われる可能性や、エラーが発生するタイミングが異なります。

9-1. 暗黙的な型変換

値が失われる可能性が低い変換は、自動的に行われることがあります。

int number = 100;long largeNumber = number;

intのすべての値はlongで表現できるため、明示的な変換は不要です。

float value = 10.5f;double result = value;

派生クラスから基底クラスへの変換も暗黙的に行えます。

Dog dog = new Dog();Animal animal = dog;

ただし、変換後の型で値を完全に表現できるとは限らない数値変換もあるため、精度が重要な処理では注意が必要です。

9-2. 明示的な型変換(キャスト)

値が失われる可能性がある変換では、キャストを明示します。

double value = 10.8;int number = (int)value;

Console.WriteLine(number); // 10

小数部分は四捨五入されず、切り捨てられます。

大きな型から小さな型へ変換すると、範囲を超える可能性があります。

long value = 3_000_000_000;int number = (int)value;

オーバーフローを検出したい場合は、checkedを使用できます。

checked{long value = 3_000_000_000;int number = (int)value;}

参照型のダウンキャストでは、実際のオブジェクトの型が一致しないと例外が発生します。

Animal animal = new Dog();Dog dog = (Dog)animal;

9-3. Parseメソッドによる変換

Parseは、文字列を数値や日付などへ変換します。

string text = "123";int number = int.Parse(text);

日付への変換も可能です。

DateTime date = DateTime.Parse("2026-06-20");

変換できない文字列を渡すと、例外が発生します。

int number = int.Parse("abc"); // FormatException

入力値が必ず正しいと保証できる場合に利用できますが、ユーザー入力にはTryParseのほうが安全です。

9-4. TryParseメソッドによる安全な変換

TryParseは、変換に成功したかどうかをboolで返します。

string input = "123";

if (int.TryParse(input, out int number)){Console.WriteLine($"変換結果: {number}");}else{Console.WriteLine("整数を入力してください");}

変換に失敗しても、通常は例外が発生しません。

小数や日付でも使用できます。

if (decimal.TryParse("1980.50", out decimal price)){Console.WriteLine(price);}

外部ファイル、フォーム、コンソールなどから受け取る値を変換するときは、基本的にTryParseを優先するとよいでしょう。

9-5. Convertクラスによる変換

Convertクラスは、さまざまな基本型を相互変換するためのメソッドを提供します。

string text = "100";int number = Convert.ToInt32(text);

数値から文字列へ変換することもできます。

int number = 100;string text = Convert.ToString(number);

小数を整数へ変換する場合、キャストとは丸め方が異なる場合があります。

double value = 10.8;

int castResult = (int)value; // 10int convertResult = Convert.ToInt32(value); // 11

Convert.ToInt32は丸め処理を行うため、切り捨てたい場合と使い分けが必要です。

9-6. is演算子とas演算子による型の確認・変換

is演算子は、オブジェクトが指定した型として扱えるかを確認します。

object value = "Hello";

if (value is string text){Console.WriteLine(text.Length);}

型の確認と変数への代入を同時に行えるため、安全で読みやすい方法です。

as演算子は、変換に成功した場合は変換後の参照を返し、失敗した場合はnullを返します。

object value = "Hello";string? text = value as string;

if (text != null){Console.WriteLine(text.Length);}

asは、主に参照型やNull許容値型で使用します。

型によって処理を分ける場合は、パターンマッチングも便利です。

static void ShowValue(object value){switch (value){case int number:Console.WriteLine($"整数: {number}");break;

    case string text:Console.WriteLine($"文字列: {text}");break;default:Console.WriteLine("その他の型");break;}

}

9-7. 型変換で発生しやすいエラー

型変換では、主に次の問題が発生します。

問題原因対策
FormatException文字列の形式が不正TryParseを使う
InvalidCastException実際の型と変換先が一致しないisで確認する
OverflowException変換先の範囲を超える範囲確認やcheckedを使う
精度の低下小数や大きな数を狭い型へ変換適切な型を選ぶ
小数部分の消失doubleなどをintへキャスト丸め方を明示する
NullReferenceExceptionnullを変換後に使用するnullを確認する

外部から受け取った値は正しいと決めつけず、変換できるかを確認してから使用することが大切です。

10. 初心者向けC#の型・変数・クラスの選び方

C#には多くの型がありますが、最初からすべてを使い分ける必要はありません。用途ごとの基本的な選び方を覚えましょう。

10-1. 整数を扱うならint

一般的な整数には、まずintを使います。

int age = 30;int quantity = 5;int score = 100;

intの範囲を超える可能性があるときだけ、longを検討します。

long totalAccessCount = 5_000_000_000L;

小さな数だからという理由だけでbyteshortを選ぶ必要はありません。通常は、APIとの互換性や計算のしやすさからintが扱いやすい型です。

10-2. 小数を扱うならdoubleまたはdecimal

一般的な小数や計測値にはdoubleを使います。

double height = 170.5;double average = 82.75;

金額にはdecimalを使います。

decimal price = 1980.50m;decimal tax = 198.05m;

ゲームやグラフィックスでメモリ使用量、速度、利用するAPIの型が重視される場合は、floatを選ぶことがあります。

10-3. 文字列を扱うならstring

氏名、住所、メッセージなどにはstringを使います。

string name = "山田太郎";string address = "東京都";string message = "登録が完了しました";

1つのcharで表現する明確な理由がない限り、文字データにはstringを選ぶほうが扱いやすいでしょう。

10-4. 複数の値をまとめるなら配列またはList

要素数が固定されている場合は配列を使います。

string[] days = { "月", "火", "水", "木", "金" };

要素数が増減する場合はList<T>を使います。

List<string> tasks = new List<string>();tasks.Add("資料作成");tasks.Add("メール送信");

初心者向けの目安は、「固定なら配列、変わるならList」です。

10-5. 関連するデータと処理をまとめるならclass

関連する複数の値と、それらを扱う処理をまとめたい場合はclassを使います。

public class Order{public int Id { get; }public decimal TotalPrice { get; private set; }

public Order(int id){Id = id;}public void AddPrice(decimal price){TotalPrice += price;}

}

商品、顧客、注文、予約など、業務上のまとまりを表すときに適しています。

10-6. 固定された選択肢を表すならenum

状態や区分など、選択肢が固定されている場合はenumを使います。

public enum UserRole{General,Manager,Administrator}

文字列で管理するよりも、入力ミスを防ぎやすくなります。

UserRole role = UserRole.Manager;

ただし、管理画面から項目を追加するなど、実行中に選択肢が変わる場合は、enumではなくデータベースのマスターデータなどで管理します。

10-7. 値を変更させたくない場合はconst・readonlyを使う

完全に固定された値にはconstを使います。

public const int MaxRetryCount = 3;

インスタンス生成時に決まり、その後は変更させない値にはreadonlyを使います。

public class Request{public readonly Guid Id;

public Request(){Id = Guid.NewGuid();}

}

プロパティの場合は、getのみ、private setinitなどを使って変更を制限する方法もあります。

public string Name { get; init; } = "";

値を変更できる範囲を必要最小限にすると、予期しない状態変更を防ぎやすくなります。

11. C#の種類に関する初心者のよくある疑問

11-1. intとlongはどちらを使うべき?

通常の整数にはintを使います。

int count = 100;

約21億を超える可能性がある場合はlongを使用します。

long fileSize = 5_000_000_000L;

データベースのID、アクセス数、大きなファイルサイズ、長期間蓄積される件数などは、longが必要になる可能性があります。

迷った場合は、実際に必要な最大値と、利用するAPIやデータベースの型を確認して選びましょう。

11-2. float・double・decimalの違いは?

主な違いは、精度、表現方法、用途です。

精度の目安主な用途
float約6~9桁ゲーム、画像、3D処理
double約15~17桁一般的な小数、科学計算
decimal約28~29桁金額、会計

一般的な小数にはdouble、金額にはdecimalを選ぶのが基本です。

double temperature = 25.6;decimal price = 1280.50m;

floatdoubleは2進浮動小数点数のため、10進数を完全には表せない場合があります。

double result = 0.1 + 0.2;Console.WriteLine(result);

小数の比較では、必要に応じて誤差を考慮しましょう。

11-3. stringとcharの違いは?

charはUTF-16コード単位を1つ保持する値型、stringは複数の文字を扱う参照型です。

char grade = 'A';string name = "Alice";

記号や区切り文字など、1つのコード単位として扱う場合はcharを使います。

char separator = ',';

氏名や文章などにはstringを使います。

string message = "こんにちは";

記述方法も異なり、charはシングルクォーテーション、stringはダブルクォーテーションで囲みます。

11-4. object・var・dynamicの違いは?

種類意味型チェック
objectすべての型の基底型コンパイル時
var右辺から具体的な型を推論コンパイル時
dynamic操作の型チェックを実行時へ遅らせる主に実行時

objectへさまざまな型を代入できますが、固有のメンバーを使うには変換が必要です。

object value = "Hello";

if (value is string text){Console.WriteLine(text.Length);}

varでは、宣言時に具体的な型が決まります。

var value = "Hello";Console.WriteLine(value.Length);

このvaluestring型であり、後から整数を代入することはできません。

dynamicでは、実行時にメンバーが解決されます。

dynamic value = "Hello";Console.WriteLine(value.Length);

安全性を考えると、通常は具体的な型またはvarを使い、dynamicは必要な場面に限定します。

11-5. classとstructの違いは?

最も基本的な違いは、classが参照型、structが値型であることです。

public class User{public string Name { get; set; } = "";}

public struct Point{public int X { get; set; }public int Y { get; set; }}

クラスは、代入すると同じインスタンスへの参照がコピーされます。構造体は、通常、値全体がコピーされます。

比較項目classstruct
分類参照型値型
代入参照をコピー値をコピー
null許容可能通常は不可
継承クラス継承が可能クラス継承は不可
適した用途状態を持つオブジェクト小さな値

一般的な業務データはclass、小さな値オブジェクトはstructという使い分けが目安です。

11-6. 配列とListの違いは?

配列は固定長、List<T>は可変長です。

int[] array = new int[3];

配列の長さは生成時に決まります。

List<int> list = new List<int>();list.Add(10);list.Add(20);

List<T>は必要に応じて要素を追加・削除できます。

要素数が確定している場合は配列、要素数が変化する場合はList<T>を選びましょう。

11-7. nullと空文字の違いは?

nullは、文字列が存在しない状態です。

string? text = null;

空文字は、文字列オブジェクトは存在するものの、長さが0の状態です。

string text = "";

空白だけの文字列も別の状態です。

string text = "   ";

それぞれを確認するメソッドがあります。

string.IsNullOrEmpty(text);string.IsNullOrWhiteSpace(text);

IsNullOrEmptynullまたは空文字を判定し、IsNullOrWhiteSpaceは空白だけの文字列も対象にします。

if (string.IsNullOrWhiteSpace(input)){Console.WriteLine("値を入力してください");}

11-8. 型を確認するにはどうすればよい?

実行時の型は、GetTypeで確認できます。

object value = "Hello";

Console.WriteLine(value.GetType());Console.WriteLine(value.GetType().Name);

指定した型かどうかは、is演算子で確認できます。

if (value is string){Console.WriteLine("string型です");}

変数へ代入しながら確認することもできます。

if (value is string text){Console.WriteLine(text.Length);}

コンパイル時の型を表すTypeオブジェクトを取得する場合は、typeofを使います。

Type type = typeof(int);Console.WriteLine(type.Name);

ジェネリックメソッド内で型を調べることもできます。

static void ShowType<T>(T value){Console.WriteLine(typeof(T).Name);}

なお、nullに対してGetTypeを呼び出すと例外が発生するため、先にnullを確認する必要があります。

if (value != null){Console.WriteLine(value.GetType().Name);}

まとめ

C#の種類を理解するときは、まず「データ型」「変数」「ユーザー定義型」を分けて考えることが重要です。

データ型は、値の種類や扱い方を決めます。変数は、その値やオブジェクトへの参照を保存するための名前です。クラスは、独自のデータ型を作り、関連するデータと処理をまとめるために使用します。

C#のデータ型は、大きく値型と参照型に分けられます。

  • 値型は、基本的に値そのものを保持する

  • 参照型は、基本的にオブジェクトへの参照を保持する

  • 値型の代入では値がコピーされる

  • 参照型の代入では参照がコピーされる

初心者が最初に覚えたい代表的な型は、次のとおりです。

用途基本的な選択
整数int
大きな整数long
一般的な小数double
金額decimal
真偽値bool
文字列string
固定長の複数データ配列
件数が変わる複数データList<T>
独自のオブジェクトclass
小さな値struct
データ中心の型record
固定された選択肢enum
共通操作の契約interface

すべての種類を一度に暗記する必要はありません。まずはintdoubleboolstring、配列、List<T>classを使えるようにし、必要に応じてstructrecordinterfacedelegateなどへ学習範囲を広げるとよいでしょう。