C#において、変数に値を格納し、プログラムの状態を管理するための最も基本的な要素が「代入演算子」です。
単に右辺の値を左辺に代入するだけでなく、計算と代入を同時に行う複合代入演算子や、近年導入されたnull合体代入演算子など、C#の進化とともにその種類と利便性は大きく向上してきました。
効率的で読みやすいコードを記述するためには、これらの演算子を適切に使い分けることが不可欠です。
本記事では、C#における代入演算子の基礎から応用まで、具体的なソースコードと実行結果を交えて詳しく解説しま す。
2026年現在の開発現場でも標準的に利用されているテクニックを網羅しているため、初心者から中級者まで幅広い開発者の方に役立つ内容となっています。
代入演算子の基本概念
代入演算子は、特定の変数に対してデータ(値)を割り当てるために使用されます。
C#において最も基本的な代入演算子は = です。
単純代入演算子 (=)
単純代入演算子は、右辺にある式の評価結果を、左辺にある変数、プロパティ、またはインデクサーに格納します。
using System;
public class Program
{
public static void Main()
{
// 基本的な代入
int age = 25;
string message = \"Hello, C#!\";
double pi = 3.14159;
Console.WriteLine($\"年齢: {age}\");
Console.WriteLine($\"メッセージ: {message}\");
Console.WriteLine($\"円周率: {pi}\");
}
}
年齢: 25
メッセージ: Hello, C#!
円周率: 3.14159
C#の代入演算子で重要な点は、代入自体が値を返す式であるということです。
これにより、以下のように複数の変数に対して同時に同じ値を代入する「連鎖代入」が可能になります。
int a, b, c;
a = b = c = 100; // すべてに100が代入される
ただし、連鎖代入はコードの可読性を損なう可能性があるため、複雑なロジックの中での多用は避けるべきです。
複合代入演算子の種類と使い方
複合代入演算子は、算術演算やビット演算と代入を一つのステップで行うための演算子です。
コードを簡潔にし、タイピング量を減らすだけでなく、「左辺の評価が一度だけ行われる」という特性を持っていま す。
算術複合代入演算子
数値計算で頻繁に利用される演算子です。
| 演算子 | 例 | 展開後の意味 |
|---|---|---|
+= | x += y | x = x + y |
-= | x -= y | x = x - y |
*= | x *= y | x = x * y |
/= | x /= y | x = x / y |
%= | x %= y | x = x % y |
実装例:算術複合代入
using System;
public class ArithmeticExample
{
public static void Main()
{
int score = 100;
score += 50; // 加算代入
Console.WriteLine($\"加算後: {score}\");
score -= 30; // 減算代入
Console.WriteLine($\"減算後: {score}\");
score *= 2; // 乗算代入
Console.WriteLine($\"乗算後: {score}\");
score /= 4; // 除算代入
Console.WriteLine($\"除算後: {score}\");
score %= 7; // 剰余代入
Console.WriteLine($\"剰余後: {score}\");
}
}
加算後: 150
減算後: 120
乗算後: 240
除算後: 60
剰余後: 4
文字列とイベントでの利用
+= 演算子は数値だけでなく、文字列の連結やイベントハンドラーの登録にも使用されます。
- 文字列の連結:
text += \"append\"とすることで、既存の文字列の末尾に新しい文字列を追加できます。 - イベントの購読: C#のデリゲートやイベントにおいて、
button.Click += OnButtonClickのように記述することで、メソッドをイベントに関連付けます。
ビット演算と論理演算の複合代入
フラグ管理や低レイヤーのプログラミングで役立つ演算子です。
| 演算子 | 意味 | 備考 |
|---|---|---|
&= | 論理積 (AND) 代入 | ビットのマスク処理などに使用 |
\ | = | 論理和 (OR) 代入 |
^= | 排他的論理和 (XOR) 代入 | ビットの反転などに使用 |
<<= | 左シフト代入 | 2のn乗倍 の計算などに使用 |
>>= | 右シフト代入 | 2のn乗分の1の計算などに使用 |
>>>= | 符号なし右シフト 代入 | C# 11で導入された符号を無視するシフト |
null合体代入演算子 (??=)
C# 8.0から導入された ??= は、「変数がnullの場合にのみ値を代入する」という非常に便利な演算子です。
これにより、nullチェックのコードを劇的に短縮できます。
従来の書き方と ??= の比較
以前は、変数がnullかどうかを確認してから値を代入するために、if文や条件演算子を使用する必要がありました。
// 従来の if 文による null チェック
if (items == null)
{
items = new List<string>();
}
// 従来の null 合体演算子による代入
items = items ?? new List<string>();
// null 合体代入演算子を使用
items ??= new List<string>();
このように、??= を使用することで、冗長な記述を排除し、意図が明確なコードになります。
実装例:遅延初期化での活用
using System;
using System.Collections.Generic;
public class LazyInitExample
{
private List<string> _data;
public void AddData(string item)
{
// _data が null の時だけインスタンスを生成する
_data ??= new List<string>();
_data.Add(item);
}
public void Display()
{
if (_data == null)
{
Console.WriteLine(\"データは空です。\");
return;
}
Console.WriteLine($\"要素数: {_data.Count}\");
}
public static void Main()
{
var example = new LazyInitExample();
example.Display();
example.AddData(\"C# 2026\");
example.Display();
}
}
データは空です。
要素数: 1
この演算子は、特に設定値のデフォルト割り当てや、リソースの遅延読み込み(Lazy Loading)の実装において非常に強力な武器となります。
タプルを用いた分解代入
C# 7.0以降、タプルを利用した「分解代入 (Deconstruction)」が可能になりました。
これにより、一度の代入操作で複数の変数に値を割り振ることができます。
分解代入の基本
using System;
public class TupleExample
{
public static void Main()
{
// タプルによる一括代入
(int x, int y) = (10, 20);
Console.WriteLine($\"x: {x}, y: {y}\");
// 既存の変数への分解代入
int width = 0;
int height = 0;
(width, height) = GetWindowSize();
Console.WriteLine($\"Width: {width}, Height: {height}\");
}
static (int, int) GetWindowSize() => (1920, 1080);
}
x: 10, y: 20
Width: 1920, Height: 1080
また、代入時に特定の値を無視したい場合は、破棄(Discard)を示すアンダースコア _ を使用します。
// 2番目の値は不要なので破棄する
(var first, _) = GetTwoValues();
参照代入 (ref assignment)
C# 7.3以降では、ref ローカル変数に対して、別の参照を再代入することが可能になりました。
これは、大きな構造体のコピーを避けたり、配列内の特定の要素を直接操作したりする際にパフォーマンス上の利点があります。
using System;
public class RefExample
{
public static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
// 配列の要素への参照を取得
ref int alias = ref numbers[2];
Console.WriteLine($\"元の値: {alias}\"); // 3
alias = 100; // 参照先 (numbers[2]) が書き換わる
Console.WriteLine($\"変更後: {numbers[2]}\"); // 100
// 別の参照に差し替える (参照代入)
alias = ref numbers[0];
alias = 500;
Console.WriteLine($\"最初の要素: {numbers[0]}\"); // 500
}
}
元の値: 3
変更後: 100
最初の要素: 500
参照代入はポインタのような柔軟性を提供しますが、安全性が保証されている点がC#の強みです。
モダンC#における代入の進化:with式
直接的な「代入演算子」そのものではありませんが、代入に関連する重要な機能として with 式があります。
これは、非破壊的な変更を伴う代入を実現します。
主にレコード(record)型で使用されますが、C# 10からは構造体(struct)でも利用可能です。
public record User(string Name, int Age);
var user1 = new User(\"Alice\", 25);
// user1 の一部の値を書き換えた新しいインスタンスを代入する
var user2 = user1 with { Age = 26 };
既存のオブジェクトを変更するのではなく、「一部のプロパティだけが異なるコピーを作成す る」という手法は、不変性(Immutability)を重視する現代のプログラミングにおいて非常に重要な役割を果たしています。
代入演算子を使用する際の注意点
代入演算子を使用する際には、いくつか注意すべき挙動があります。
1. 演算の優先順位
代入演算子は、ほとんどの演算子よりも優先順位が低く設定されています。
そのため、右辺の計算がすべて終わってから代入が行われます。
例えば、x = y + z * 2; では、まず掛け算が行われ、次に足し算、最後に代入が行われます。
2. 型の互換性
C#は強い型付けを持つ言語であるため、代入演算子の両辺の型には互換性がなければなりません。
- 小さい整数型から大きい整数型への代入(例:
intからlong)は暗黙的に行われます。 - 逆(例:
longからint)は明示的なキャスト(int)が必要です。
3. オブジェクトの参照コピー
クラス(参照型)の変数に対して代入を行うと、「インスタンスそのもの」ではなく「参照(メモリ上の位置)」がコピーされます。
var list1 = new List<int> { 1, 2 };
var list2 = list1; // 参照がコピーされる
list2.Add(3);
// list1 も list2 と同じインスタンスを指しているため、list1 も変更される
Console.WriteLine(list1.Count); // 出力: 3
この挙動を理解していないと、意図しないバグの原因になるため注意が必要です。
まとめ
C#の代入演算子は、単なる値の格納手段を超えて、コードの簡潔性や安全性を高めるための強力なツールへと進化してきました。
- 基本代入 (=): すべての基礎であり、連鎖代入も可能。
- 複合代入 (+=, -= など): 計算と代入を効率的に記述し、可読性を向上させる。
- null合体代入 (??=): モダンC#においてnull安全なコードを短く書くための必須知識。
- 分解代入: タプルを活用して複数の値をスマートに処理。
- 参照代入: パフォーマンスを重視するシナリオでの高度なメモリ操作。
これらの演算子をマスターすることで、冗長なコードを排除し、2026年の標準的な開発スタイルに適合した高品質なプログラムを記述できるようになります。
まずは日常的なコードの中で、??= や複合代入を積極的に活用することから始めてみてください。
