csharp operators完全ガイド|C#演算子の種類・優先順位・使い方をサンプルコードで解説

はじめに

「csharp operators」は、C#で値を計算したり、比較したり、代入したり、nullを安全に扱ったりするための演算子を指します。C#演算子は、+==&&???.=>など、短い記号で多くの処理を表現できる重要な文法です。

C#では、演算子の優先順位や結合規則によって式の評価順が決まります。たとえば、2 + 3 * 4は掛け算が先に評価されるため、結果は20ではなく14になります。Microsoft Learnでも、演算子の優先順位と結合規則が式の評価順を決め、括弧で評価順を変更できると説明されています。Microsoft Learn

この記事では、C# operatorsの種類、使い方、優先順位、null関連演算子、型チェック、ビット演算子、演算子オーバーロードまで、サンプルコードを交えてわかりやすく解説します。

1. csharp operatorsとは?C#演算子の基本を初心者向けに解説

csharp operatorsとは、C#の式の中で計算・比較・代入・条件判定・型判定などを行うための記号やキーワードです。たとえば、a + b+x == y==name ?? "未設定"??が演算子です。

C#演算子を理解すると、if文、for文、LINQ、非同期処理、nullチェック、クラス設計など、C#プログラミングの多くのコードを読みやすくなります。

1-1. C#における演算子の役割

C#演算子の主な役割は、値に対して何らかの操作を行い、結果を返すことです。

C#
int a = 10;
int b = 3;

int sum = a + b; // 加算
bool result = a > b; // 比較
a += 5; // 複合代入

この例では、+は数値を足し、>は大小を比較し、+=は変数に値を加算して再代入しています。

演算子は単なる記号ではなく、C#の式を構成する中心的な要素です。C#の式は、リテラル、変数、演算子を組み合わせて作られます。C#仕様では、演算子には単項演算子、二項演算子、三項演算子があると説明されています。Microsoft Learn

1-2. 演算子・オペランド・式の関係

演算子を理解するには、「演算子」「オペランド」「式」の関係を押さえることが大切です。

C#
int result = 10 + 5;

このコードでは、+が演算子、105がオペランド、10 + 5全体が式です。

オペランドとは、演算子の対象になる値です。+のように2つのオペランドを取る演算子は二項演算子、!isValid!のように1つのオペランドを取る演算子は単項演算子、condition ? a : b?:は3つのオペランドを取る三項演算子です。

C#
int x = 10;

int positive = +x; // 単項演算子
int total = x + 20; // 二項演算子
string text = x > 0 ? "正" : "負"; // 三項演算子

1-3. C# operatorsを学ぶとコード理解が速くなる理由

C# operatorsを学ぶと、コードを読むスピードが上がります。理由は、C#のコードには演算子が頻繁に登場するからです。

たとえば、次のコードを見てください。

C#
if (user != null && user.IsActive)
{
Console.WriteLine(user.Name ?? "名前なし");
}

この短いコードには、!=&&.??という複数の演算子が含まれています。演算子の意味がわかれば、「userがnullではなく、かつ有効なユーザーなら、名前を表示し、名前がnullなら代替文字列を使う」とすぐに読み取れます。

演算子はC#の文法の土台です。初心者が早い段階で学ぶほど、サンプルコードや実務コードを理解しやすくなります。

2. C#演算子の種類一覧

C#には多くの演算子があります。すべてを一度に暗記する必要はありませんが、種類ごとに役割を整理しておくと学習しやすくなります。

2-1. 算術演算子

算術演算子は、数値計算に使います。

演算子意味
+加算a + b
-減算a - b
*乗算a * b
/除算a / b
%剰余a % b
++インクリメントx++
--デクリメントx--
C#
int a = 10;
int b = 3;

Console.WriteLine(a + b); // 13
Console.WriteLine(a - b); // 7
Console.WriteLine(a * b); // 30
Console.WriteLine(a / b); // 3
Console.WriteLine(a % b); // 1

2-2. 代入演算子

代入演算子は、変数に値を入れるために使います。

演算子意味
=代入x = 10
+=加算して代入x += 5
-=減算して代入x -= 5
*=乗算して代入x *= 2
/=除算して代入x /= 2
%=剰余を代入x %= 3
??=nullの場合だけ代入name ??= "未設定"
C#
int x = 10;
x += 5;

Console.WriteLine(x); // 15

2-3. 比較演算子

比較演算子は、2つの値を比較してbool値を返します。

演算子意味
==等しい
!=等しくない
<より小さい
>より大きい
<=以下
>=以上
C#
int score = 80;

Console.WriteLine(score >= 60); // True
Console.WriteLine(score == 100); // False

2-4. 論理演算子

論理演算子は、複数の条件を組み合わせるときに使います。

演算子意味
&&かつ
`
!否定
&論理ANDまたはビットAND
``
C#
bool isAdult = true;
bool hasTicket = false;

Console.WriteLine(isAdult && hasTicket); // False
Console.WriteLine(isAdult || hasTicket); // True
Console.WriteLine(!hasTicket); // True

2-5. ビット演算子

ビット演算子は、整数を2進数のビット単位で操作します。

演算子意味
&ビットAND
``
^ビットXOR
~ビット反転
<<左シフト
>>右シフト
>>>符号なし右シフト

~<<>>>>>&|^は、ユーザー定義型でオーバーロード可能なビット演算子・シフト演算子として扱われます。Microsoft Learn

2-6. 条件演算子

条件演算子?:は、条件によって返す値を切り替える演算子です。

C#
int score = 75;
string result = score >= 60 ? "合格" : "不合格";

Console.WriteLine(result); // 合格

?:は三項条件演算子とも呼ばれ、条件がtrueなら1つ目の式、falseなら2つ目の式を評価します。Microsoft Learn

2-7. null関連演算子

C#では、nullを安全に扱うための演算子が用意されています。

演算子意味
??左辺がnullでなければ左辺、nullなら右辺
??=左辺がnullの場合だけ右辺を代入
?.nullでなければメンバーアクセス
?[]nullでなければインデックスアクセス
!null許容警告を抑制するnull免除演算子
C#
string? name = null;
Console.WriteLine(name ?? "未設定"); // 未設定

2-8. 型チェック・型変換演算子

型に関する演算子は、オブジェクトの型を調べたり、別の型へ変換したりするときに使います。

演算子意味
is指定した型かどうかを判定
as変換できる場合だけ変換、できなければnull
typeof型情報を取得
sizeof型のサイズを取得
(型)キャスト
C#
object value = "Hello";

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

2-9. その他のよく使う演算子

C#では、以下のような演算子もよく使います。

演算子意味
.メンバーアクセス
[]配列・インデクサーアクセス
()メソッド呼び出し
newインスタンス生成
nameof識別子名を文字列として取得
await非同期処理の完了を待つ
=>ラムダ式
..範囲
^末尾からのインデックス

.[]?.?[]()^..は、メンバーアクセスやインデックスアクセス、範囲指定などで使われる演算子として整理されています。Microsoft Learn

3. 算術演算子の使い方

算術演算子は、C# operatorsの中でも最初に覚えるべき基本です。数値計算だけでなく、ループ処理、集計、条件判定などでも頻繁に使います。

3-1. 足し算・引き算・掛け算・割り算

基本的な四則演算は、数学と同じ感覚で書けます。

C#
int a = 20;
int b = 6;

Console.WriteLine(a + b); // 26
Console.WriteLine(a - b); // 14
Console.WriteLine(a * b); // 120
Console.WriteLine(a / b); // 3

int同士の割り算では、結果もintになります。そのため、20 / 6の結果は3.333...ではなく3です。

小数を得たい場合は、doubledecimalを使います。

C#
double a = 20;
double b = 6;

Console.WriteLine(a / b); // 3.3333333333333335

3-2. 剰余演算子の使い方

剰余演算子%は、割り算の余りを求めます。

C#
int number = 17;

Console.WriteLine(number % 5); // 2

剰余演算子は、偶数・奇数の判定でよく使われます。

C#
int number = 10;

if (number % 2 == 0)
{
Console.WriteLine("偶数です");
}
else
{
Console.WriteLine("奇数です");
}

また、一定周期で処理を切り替えたい場合にも便利です。

C#
for (int i = 1; i <= 10; i++)
{
if (i % 3 == 0)
{
Console.WriteLine($"{i}: 3の倍数");
}
}

3-3. インクリメント・デクリメント

++は値を1増やす演算子、--は値を1減らす演算子です。

C#
int count = 0;

count++;
Console.WriteLine(count); // 1

count--;
Console.WriteLine(count); // 0

インクリメントには、前置と後置があります。

C#
int x = 5;

Console.WriteLine(++x); // 6
Console.WriteLine(x); // 6
C#
int y = 5;

Console.WriteLine(y++); // 5
Console.WriteLine(y); // 6

++xは先に増やしてから値を使い、x++は値を使ってから増やします。for文では後置インクリメントがよく使われます。

C#
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}

3-4. 整数除算で注意すべきポイント

C#初心者がつまずきやすいのが整数除算です。

C#
int a = 5;
int b = 2;

Console.WriteLine(a / b); // 2

5 / 2は数学では2.5ですが、int同士の割り算なので小数点以下が切り捨てられます。

小数結果が必要な場合は、どちらか一方をdoubleにします。

C#
int a = 5;
int b = 2;

Console.WriteLine((double)a / b); // 2.5

または最初からdouble型で宣言します。

C#
double a = 5;
double b = 2;

Console.WriteLine(a / b); // 2.5

3-5. 算術演算子のサンプルコード

以下は、商品価格、数量、割引率から合計金額を計算する例です。

C#
int price = 1200;
int quantity = 3;
double discountRate = 0.1;

int subtotal = price * quantity;
double discount = subtotal * discountRate;
double total = subtotal - discount;

Console.WriteLine($"小計: {subtotal}円");
Console.WriteLine($"割引: {discount}円");
Console.WriteLine($"合計: {total}円");

税込価格を計算するなら、次のように書けます。

C#
decimal price = 1000m;
decimal taxRate = 0.10m;

decimal tax = price * taxRate;
decimal total = price + tax;

Console.WriteLine($"税込価格: {total}円");

金額計算では、誤差を避けるためにdoubleではなくdecimalを使うことが一般的です。

4. 比較演算子と論理演算子の使い方

比較演算子と論理演算子は、if文、while文、条件分岐で頻繁に使います。C#の条件式は最終的にbool型、つまりtrueまたはfalseになります。

4-1. 等価・不等価・大小比較

比較演算子は、2つの値の関係を判定します。

C#
int age = 20;

Console.WriteLine(age == 20); // True
Console.WriteLine(age != 18); // True
Console.WriteLine(age > 18); // True
Console.WriteLine(age < 18); // False
Console.WriteLine(age >= 20); // True
Console.WriteLine(age <= 19); // False

==は「等しいか」、!=は「等しくないか」を判定します。=は代入なので、==と混同しないように注意しましょう。

文字列比較にも==を使えます。

C#
string role = "Admin";

if (role == "Admin")
{
Console.WriteLine("管理者です");
}

4-2. &&と||の使い方

&&は「かつ」、||は「または」を表します。

C#
int age = 25;
bool hasLicense = true;

if (age >= 18 && hasLicense)
{
Console.WriteLine("運転できます");
}

この例では、age >= 18hasLicenseの両方がtrueの場合だけ処理されます。

||はどちらか一方がtrueならtrueになります。

C#
bool isMember = false;
bool hasCoupon = true;

if (isMember || hasCoupon)
{
Console.WriteLine("割引対象です");
}

4-3. !演算子で条件を反転する

!は真偽値を反転する演算子です。

C#
bool isDeleted = false;

if (!isDeleted)
{
Console.WriteLine("表示できます");
}

isDeletedfalseなので、!isDeletedtrueになります。

メソッド名と組み合わせると読みやすいコードになります。

C#
bool isValid = false;

if (!isValid)
{
Console.WriteLine("入力内容が不正です");
}

4-4. &と&&、|と||の違い

&&||は短絡評価を行います。短絡評価とは、左側だけで結果が決まる場合、右側を評価しない仕組みです。Microsoft Learnでも、&&||????=?.?[]?:は条件に応じて一部のオペランドだけを評価する演算子として説明されています。Microsoft Learn

C#
string? text = null;

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

この場合、text != nullfalseなら、text.Length > 0は評価されません。そのため、null参照例外を避けられます。

一方、&|boolに使うと、左右両方が評価されます。

C#
bool A()
{
Console.WriteLine("A");
return false;
}

bool B()
{
Console.WriteLine("B");
return true;
}

Console.WriteLine(A() && B()); // Aだけ表示
Console.WriteLine(A() & B()); // AとBが表示

条件式では、基本的に&&||を使うのが安全で一般的です。

4-5. if文でよく使う演算子の実例

ユーザーのログイン判定を例にします。

C#
string? userName = "taro";
bool isActive = true;
bool isLocked = false;

if (!string.IsNullOrEmpty(userName) && isActive && !isLocked)
{
Console.WriteLine("ログインできます");
}
else
{
Console.WriteLine("ログインできません");
}

このコードでは、以下の条件をすべて満たす場合だけログインできます。

条件意味
!string.IsNullOrEmpty(userName)ユーザー名が空ではない
isActiveアカウントが有効
!isLockedロックされていない

複数条件を組み合わせる場合は、読みやすさのために括弧を使うのもおすすめです。

C#
if ((isActive && !isLocked) || userName == "admin")
{
Console.WriteLine("アクセス許可");
}

5. 代入演算子と複合代入演算子

代入演算子は、変数に値を保存するために使います。C#のコードでは非常に頻繁に登場します。

5-1. =演算子の基本

=は右辺の値を左辺の変数に代入します。

C#
int number = 10;
string name = "Alice";
bool isActive = true;

=は比較ではありません。比較には==を使います。

C#
int x = 10;

if (x == 10)
{
Console.WriteLine("xは10です");
}

C#では、if (x = 10)のようにintを条件式として使うことはできないため、CやC++よりもこの種のミスは検出されやすいです。ただし、bool変数では注意が必要です。

C#
bool isEnabled = false;

if (isEnabled = true)
{
Console.WriteLine("常に実行されます");
}

このコードはコンパイルできてしまうため、条件判定では===の違いに注意しましょう。

5-2. +=、-=、*=、/=、%=の使い方

複合代入演算子は、計算と代入をまとめて書ける演算子です。

C#
int x = 10;

x += 5; // x = x + 5
x -= 3; // x = x - 3
x *= 2; // x = x * 2
x /= 4; // x = x / 4
x %= 3; // x = x % 3

Console.WriteLine(x);

文字列にも+=を使えます。

C#
string message = "Hello";
message += ", ";
message += "C#";

Console.WriteLine(message); // Hello, C#

ただし、大量の文字列結合ではStringBuilderを使うほうが効率的です。

5-3. 代入式の戻り値

C#では、代入式そのものにも値があります。

C#
int x;
int y;

x = y = 10;

Console.WriteLine(x); // 10
Console.WriteLine(y); // 10

代入演算子は右結合です。つまり、x = y = 10x = (y = 10)として評価されます。Microsoft Learnでも、代入演算子、null合体演算子、ラムダ、条件演算子?:は右結合であると説明されています。Microsoft Learn

代入式の戻り値を利用するコードは便利な場合もありますが、読みやすさを損なうこともあります。初心者のうちは、無理に使わずシンプルに書くのがおすすめです。

5-4. 複合代入演算子を使うメリット

複合代入演算子のメリットは、コードが短く読みやすくなることです。

C#
total = total + price;

これは次のように書けます。

C#
total += price;

カウンターや合計値の更新では特によく使います。

C#
int total = 0;

foreach (int score in new[] { 80, 90, 70 })
{
total += score;
}

Console.WriteLine(total); // 240

また、C# 14以降では、ユーザー定義型が複合代入演算子を明示的にオーバーロードできるようになり、値をインプレースで更新する効率的な実装に使えると説明されています。Microsoft Learn

5-5. 代入演算子で起きやすいミス

よくあるミスは、比較のつもりで=を書いてしまうことです。

C#
bool isAdmin = false;

if (isAdmin = true)
{
Console.WriteLine("管理者です");
}

このコードは、isAdmintrueを代入しているため、条件が常にtrueになります。正しくは次のように書きます。

C#
if (isAdmin == true)
{
Console.WriteLine("管理者です");
}

ただし、bool変数は次のように書くほうが自然です。

C#
if (isAdmin)
{
Console.WriteLine("管理者です");
}

否定なら次のように書きます。

C#
if (!isAdmin)
{
Console.WriteLine("管理者ではありません");
}

6. 条件演算子・null関連演算子

条件演算子とnull関連演算子は、C#らしい簡潔なコードを書くために欠かせません。特に、null安全なコードを書くうえで????=?.?[]は非常に重要です。

6-1. 三項条件演算子?:の使い方

三項条件演算子?:は、条件に応じて値を返します。

C#
int score = 82;

string result = score >= 60 ? "合格" : "不合格";

Console.WriteLine(result); // 合格

基本構文は次のとおりです。

C#
条件 ? trueの場合の値 : falseの場合の値

if文で書くと次のようになります。

C#
string result;

if (score >= 60)
{
result = "合格";
}
else
{
result = "不合格";
}

値を選ぶだけなら、?:を使うとコードが短くなります。ただし、条件が複雑な場合はif文のほうが読みやすいこともあります。

6-2. null合体演算子??の使い方

??は、左辺がnullでなければ左辺を返し、nullなら右辺を返します。Microsoft Learnでは、??は左辺がnullでない場合に左辺の値を返し、nullの場合だけ右辺を評価すると説明されています。Microsoft Learn

C#
string? name = null;

string displayName = name ?? "ゲスト";

Console.WriteLine(displayName); // ゲスト

null許容型にも使えます。

C#
int? count = null;

int actualCount = count ?? 0;

Console.WriteLine(actualCount); // 0

メソッドの引数や表示用テキストの初期値としてよく使います。

C#
void PrintName(string? name)
{
Console.WriteLine(name ?? "名前未設定");
}

6-3. null合体代入演算子??=の使い方

??=は、左辺がnullの場合だけ右辺を代入します。

C#
List<string>? names = null;

names ??= new List<string>();
names.Add("Alice");

Console.WriteLine(names.Count); // 1

これは次のコードを短くしたものです。

C#
if (names == null)
{
names = new List<string>();
}

??=の左辺は、変数、プロパティ、インデクサー要素である必要があります。Microsoft Learn

C#
Dictionary<string, int>? scores = null;

scores ??= new Dictionary<string, int>();
scores["Math"] = 90;

6-4. null条件演算子?.と?[]の使い方

?.は、左辺がnullでなければメンバーにアクセスし、nullなら結果としてnullを返します。

C#
User? user = null;

Console.WriteLine(user?.Name); // null

通常の.を使うと、userがnullの場合にNullReferenceExceptionが発生します。

C#
Console.WriteLine(user.Name); // userがnullなら例外

?[]は、配列やリストなどにnull安全にアクセスするときに使います。

C#
string[]? names = null;

Console.WriteLine(names?[0]); // null

?.??を組み合わせると、nullの場合の代替値も指定できます。

C#
string displayName = user?.Name ?? "ゲスト";

6-5. nullチェックを短く書くサンプルコード

ユーザー情報を安全に表示する例です。

C#
class User
{
public string? Name { get; set; }
public Address? Address { get; set; }
}

class Address
{
public string? City { get; set; }
}

User? user = null;

string city = user?.Address?.City ?? "住所未登録";

Console.WriteLine(city);

userAddressCityのどれかがnullでも例外にならず、"住所未登録"が使われます。

リストの初期化にも便利です。

C#
List<string>? logs = null;

(logs ??= new List<string>()).Add("処理開始");

Console.WriteLine(logs.Count); // 1

null関連演算子を使うと、冗長なnullチェックを減らしつつ、安全なコードを書けます。

7. 型に関する演算子

型に関する演算子は、オブジェクトの型を判定したり、型情報を取得したり、型変換したりするときに使います。

7-1. is演算子の使い方

is演算子は、オブジェクトが指定した型に一致するかどうかを判定します。

C#
object value = "Hello";

if (value is string)
{
Console.WriteLine("文字列です");
}

C#では、パターンマッチングと組み合わせて、型チェックと変数宣言を同時に行えます。

C#
object value = "Hello";

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

is nullもよく使われます。

C#
string? name = null;

if (name is null)
{
Console.WriteLine("nameはnullです");
}

否定する場合はis not nullが読みやすいです。

C#
if (name is not null)
{
Console.WriteLine(name.Length);
}

7-2. as演算子の使い方

as演算子は、変換できる場合は指定型に変換し、変換できない場合はnullを返します。

C#
object value = "Hello";

string? text = value as string;

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

通常のキャストでは、変換できない場合に例外が発生します。

C#
object value = 123;

// string text = (string)value; // InvalidCastException

asは参照型やnull許容値型で使われます。変換後にnullチェックが必要です。

C#
object obj = new List<int>();

List<int>? numbers = obj as List<int>;

if (numbers is not null)
{
Console.WriteLine(numbers.Count);
}

7-3. typeof演算子の使い方

typeof演算子は、型そのものの情報を表すSystem.Typeオブジェクトを取得します。

C#
Type type = typeof(string);

Console.WriteLine(type.Name); // String
Console.WriteLine(type.FullName); // System.String

リフレクション、属性、型比較などで使います。

C#
object value = "Hello";

if (value.GetType() == typeof(string))
{
Console.WriteLine("string型です");
}

ただし、単純な型判定ではisを使うほうが自然です。

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

7-4. sizeof演算子の使い方

sizeof演算子は、型のサイズをバイト単位で取得します。Microsoft Learnでは、sizeofはマネージドメモリ上で共通言語ランタイムが割り当てるバイト数を返すと説明されています。Microsoft Learn

C#
Console.WriteLine(sizeof(int));    // 4
Console.WriteLine(sizeof(double)); // 8
Console.WriteLine(sizeof(char)); // 2

基本的な組み込み型では安全なコードで使えますが、任意の構造体などで使う場合はunsafeコンテキストが必要になることがあります。

C#
unsafe
{
Console.WriteLine(sizeof(MyStruct));
}

struct MyStruct
{
public int X;
public double Y;
}

通常のアプリケーション開発では、sizeofを頻繁に使う場面は多くありません。低レベルなメモリ処理やパフォーマンスを意識する場面で使います。

7-5. キャスト演算子との違い

キャスト演算子は、値を別の型として扱うために使います。

C#
double value = 10.5;
int number = (int)value;

Console.WriteLine(number); // 10

明示的キャストでは、情報が失われることがあります。上の例では、小数点以下が切り捨てられます。

isas、キャストの違いを整理すると次のようになります。

演算子用途失敗時
is型かどうか判定false
as安全に参照型へ変換null
(型)明示的に変換例外または値変換
C#
object obj = "C#";

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

string? text2 = obj as string;
string text3 = (string)obj;

安全性を重視するならisパターン、変換失敗時にnullで扱いたいならas、確実に変換できるとわかっているならキャストを使います。

8. ビット演算子とシフト演算子

ビット演算子は、整数を2進数のビット単位で扱う演算子です。通常の業務アプリでは使用頻度が低い場合もありますが、フラグ管理、権限管理、低レベル処理、パフォーマンス重視の処理で役立ちます。

8-1. &、|、^、~の意味

ビット演算子の意味は次のとおりです。

演算子意味説明
&AND両方のビットが1なら1
``OR
^XOR片方だけが1なら1
~NOTビットを反転
C#
int a = 0b_1100; // 12
int b = 0b_1010; // 10

Console.WriteLine(a & b); // 8 = 1000
Console.WriteLine(a | b); // 14 = 1110
Console.WriteLine(a ^ b); // 6 = 0110
Console.WriteLine(~a); // ビット反転

&|は、boolの論理演算にも使えますが、条件式では短絡評価のある&&||を使うのが一般的です。

8-2. <<と>>の使い方

<<は左シフト、>>は右シフトです。

C#
int x = 4; // 0100

Console.WriteLine(x << 1); // 8
Console.WriteLine(x >> 1); // 2

左シフトは、ビットを左にずらします。単純な正の整数では、1ビット左にずらすと2倍、右にずらすと2分の1のような結果になります。

C#
int value = 1;

Console.WriteLine(value << 1); // 2
Console.WriteLine(value << 2); // 4
Console.WriteLine(value << 3); // 8

C#には>>>という符号なし右シフト演算子もあります。符号ビットを維持する>>とは異なり、左側に0を詰める右シフトです。

8-3. ビット演算が使われる場面

ビット演算は、次のような場面で使われます。

場面
フラグ管理読み取り権限、書き込み権限、実行権限
低レベル処理バイナリデータ、通信プロトコル
ゲーム開発状態管理、レイヤー管理
パフォーマンス最適化大量の真偽値をビットで管理
Enumの組み合わせ[Flags]属性を使った列挙型

特に、複数のON/OFF状態を1つの値にまとめたい場合に便利です。

8-4. フラグ管理のサンプルコード

[Flags]属性を使うと、列挙型をビットフラグとして扱いやすくなります。

C#
[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}

Permission permission = Permission.Read | Permission.Write;

Console.WriteLine(permission); // Read, Write

特定の権限を持っているか確認するには、&を使います。

C#
if ((permission & Permission.Read) == Permission.Read)
{
Console.WriteLine("読み取り可能");
}

権限を追加するには|=を使います。

C#
permission |= Permission.Execute;

権限を削除するには、&~を組み合わせます。

C#
permission &= ~Permission.Write;

8-5. ビット演算子を使うときの注意点

ビット演算子は強力ですが、読みやすさが落ちやすいという注意点があります。

C#
if ((flags & 4) != 0)
{
// 4が何を意味するかわかりにくい
}

このようにマジックナンバーを直接使うと、後から読んだときに意味がわかりません。列挙型を使って意味を明確にしましょう。

C#
if ((permission & Permission.Execute) == Permission.Execute)
{
Console.WriteLine("実行可能");
}

また、&&&|||を混同しないように注意が必要です。条件式では基本的に&&||、ビット操作では&|を使うと覚えるとよいでしょう。

9. C#演算子の優先順位と結合規則

C#演算子の優先順位は、式の結果に大きく影響します。複雑な式を書くときは、どの演算子が先に評価されるかを理解しておく必要があります。

9-1. 演算子の優先順位とは

演算子の優先順位とは、複数の演算子が1つの式に含まれる場合に、どの演算子を先に評価するかを決めるルールです。

C#
int result = 2 + 3 * 4;

Console.WriteLine(result); // 14

+より*の優先順位が高いため、先に3 * 4が評価されます。

括弧を使うと評価順を変えられます。

C#
int result = (2 + 3) * 4;

Console.WriteLine(result); // 20

Microsoft Learnでも、優先順位の高い演算子が低い演算子より先に評価され、括弧で評価順を変更できると説明されています。Microsoft Learn

9-2. 結合規則とは

結合規則とは、同じ優先順位の演算子が並んだときに、左から評価するか右から評価するかを決めるルールです。

多くの二項演算子は左結合です。

C#
int result = 10 - 3 - 2;

Console.WriteLine(result); // 5

これは次のように評価されます。

C#
(10 - 3) - 2

一方、代入演算子は右結合です。

C#
int a;
int b;

a = b = 10;

これは次のように評価されます。

C#
a = (b = 10);

C#では、代入演算子、null合体演算子、ラムダ、条件演算子?:が右結合です。Microsoft Learn

9-3. 優先順位一覧表

代表的なC#演算子の優先順位を、高い順に整理します。

優先順位演算子
x.yx?.yx?[y]x++x--newtypeofnameof
+x-x!x~x++x--x(T)xawait
*/%
+-
<<>>>>>
<><=>=isas
==!=
&
^
`
&&
`
??
?:
=+=-=*=/=%=??==>

この表は大まかな理解に使えます。実際のコードでは、優先順位を暗記するよりも、迷ったら括弧を使うほうが安全です。

9-4. &&と||、??、?:の優先順位

条件式で特に注意したいのが、&&||???:の優先順位です。

C#
bool result = true || false && false;

Console.WriteLine(result); // True

&&||より優先順位が高いため、次のように評価されます。

C#
true || (false && false)

???:を組み合わせる場合も、読みやすさのために括弧を使うのがおすすめです。

C#
string? name = null;
bool isGuest = true;

string displayName = name ?? (isGuest ? "ゲスト" : "未設定");

Console.WriteLine(displayName);

??は右結合です。つまり、a ?? b ?? ca ?? (b ?? c)として評価されます。Microsoft Learn

9-5. 迷ったら括弧を使うべき理由

演算子の優先順位をすべて暗記していても、読み手が同じように覚えているとは限りません。保守しやすいコードを書くには、意図が明確に伝わることが重要です。

C#
if (isAdmin || isManager && isActive)
{
Console.WriteLine("アクセス許可");
}

このコードは、&&が先に評価されるため、次の意味になります。

C#
if (isAdmin || (isManager && isActive))
{
Console.WriteLine("アクセス許可");
}

もし「管理者またはマネージャーで、かつ有効」という意味にしたいなら、次のように書くべきです。

C#
if ((isAdmin || isManager) && isActive)
{
Console.WriteLine("アクセス許可");
}

括弧を使うことで、バグを防ぎ、レビュー時の誤解も減らせます。

9-6. 優先順位で間違えやすいコード例

よくある間違いは、条件の組み合わせです。

C#
bool isAdmin = false;
bool isManager = true;
bool isActive = false;

if (isAdmin || isManager && isActive)
{
Console.WriteLine("許可");
}
else
{
Console.WriteLine("拒否");
}

このコードは次のように評価されます。

C#
if (isAdmin || (isManager && isActive))

結果はfalseです。

しかし、意図が「管理者またはマネージャーなら、アクティブ状態を確認する」なら、次のように書く必要があります。

C#
if ((isAdmin || isManager) && isActive)
{
Console.WriteLine("許可");
}
else
{
Console.WriteLine("拒否");
}

もう1つの例です。

C#
int result = 13 / 5 / 2;
Console.WriteLine(result); // 1

これは左結合なので、次のように評価されます。

C#
(13 / 5) / 2

括弧を変えると結果も変わります。

C#
int result = 13 / (5 / 2);
Console.WriteLine(result); // 6

10. C#の特殊な演算子

C#には、計算や比較以外にも便利な特殊演算子があります。実務コードでは、nameofawait=>..^checkeduncheckedなどをよく見かけます。

10-1. nameof演算子

nameofは、変数名、型名、メンバー名を文字列として取得する演算子です。Microsoft Learnでは、nameof式はオペランドの名前を生成し、コンパイル時に評価され、実行時の影響はないと説明されています。Microsoft Learn

C#
string name = "Alice";

Console.WriteLine(nameof(name)); // name

例外メッセージやログでよく使います。

C#
void SetName(string? name)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
}

文字列を直接書くより、リファクタリングに強くなります。

C#
throw new ArgumentNullException("name");

このように書くと、変数名を変更したときに文字列だけ古いまま残る可能性があります。nameof(name)なら、名前変更に追従しやすくなります。

10-2. await演算子

awaitは、非同期処理の完了を待つための演算子です。Microsoft Learnでは、awaitは非同期操作が完了するまで、それを含むasyncメソッドの評価を中断し、スレッドをブロックしないと説明されています。Microsoft Learn

C#
async Task<string> GetTextAsync(HttpClient client)
{
string text = await client.GetStringAsync("https://example.com");
return text;
}

awaitを使うメソッドには、通常asyncを付けます。

C#
async Task RunAsync()
{
await Task.Delay(1000);
Console.WriteLine("1秒後に表示");
}

awaitは非同期処理を同期処理のように読みやすく書ける重要な演算子です。

10-3. lambda演算子=>

=>はラムダ演算子です。左側に引数、右側に処理を書きます。Microsoft Learnでは、ラムダ式は左側に入力パラメーター、右側に式またはステートメントブロックを指定して作成すると説明されています。Microsoft Learn

C#
Func<int, int> square = x => x * x;

Console.WriteLine(square(5)); // 25

複数の引数を使う場合は括弧で囲みます。

C#
Func<int, int, int> add = (a, b) => a + b;

Console.WriteLine(add(3, 4)); // 7

LINQでも頻繁に登場します。

C#
var numbers = new[] { 1, 2, 3, 4, 5 };

var evenNumbers = numbers.Where(x => x % 2 == 0);

foreach (var n in evenNumbers)
{
Console.WriteLine(n);
}

10-4. range演算子..とindex演算子^

..は範囲演算子、^は末尾からのインデックスを表す演算子です。Microsoft Learnでは、^はシーケンスの末尾からの位置を示し、..は範囲のインデックス指定に使うと説明されています。Microsoft Learn

C#
int[] numbers = { 10, 20, 30, 40, 50 };

Console.WriteLine(numbers[^1]); // 50

^1は最後の要素、^2は最後から2番目の要素を表します。

C#
int[] selected = numbers[1..4];

foreach (int n in selected)
{
Console.WriteLine(n); // 20, 30, 40
}

1..4は、インデックス1以上、4未満の範囲を表します。

文字列にも使えます。

C#
string text = "CSharp";

Console.WriteLine(text[..1]); // C
Console.WriteLine(text[1..]); // Sharp
Console.WriteLine(text[^1]); // p

10-5. switch式で使う演算子

switch式では、=>を使って条件と結果を結びます。

C#
int score = 85;

string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "D"
};

Console.WriteLine(grade); // B

switch式は、値に応じて結果を返したいときに便利です。

C#
string GetMessage(string role) => role switch
{
"Admin" => "管理者",
"User" => "一般ユーザー",
_ => "不明"
};

_はどの条件にも一致しない場合のデフォルトパターンです。

10-6. checkedとunchecked

checkeduncheckedは、整数演算のオーバーフローをチェックするかどうかを制御します。

C#
int max = int.MaxValue;

checked
{
// int result = max + 1; // OverflowException
}

uncheckedでは、オーバーフローを許可します。

C#
int max = int.MaxValue;

unchecked
{
int result = max + 1;
Console.WriteLine(result); // int.MinValue
}

通常のアプリケーションでは頻繁に使う演算子ではありませんが、数値の上限を扱う処理や低レベルな計算では重要です。

11. 演算子オーバーロード

演算子オーバーロードとは、独自の型に対して+==などの演算子の動作を定義する機能です。適切に使うと、独自クラスや構造体を自然な構文で扱えます。

11-1. 演算子オーバーロードとは

演算子オーバーロードを使うと、ユーザー定義型に対して既存のC#演算子の意味を定義できます。Microsoft Learnでは、ユーザー定義型で定義済みC#演算子をオーバーロードし、その型がオペランドに含まれる場合のカスタム実装を提供できると説明されています。Microsoft Learn

たとえば、2次元ベクトル同士を+で足せるようにできます。

C#
Vector a = new Vector(1, 2);
Vector b = new Vector(3, 4);

Vector c = a + b;

このように書けると、数学的な意味が自然に表現できます。

11-2. オーバーロードできる演算子

C#では、すべての演算子をオーバーロードできるわけではありません。たとえば、算術演算子、比較演算子、等価演算子、ビット演算子などはオーバーロードできます。

代表例は次のとおりです。

種類演算子
単項演算子+-!~++--
算術演算子+-*/%
比較演算子<><=>=
等価演算子==!=
ビット演算子&、`
変換演算子implicitexplicit

==!=をオーバーロードする場合は、片方だけでなく両方を定義する必要があります。Microsoft Learn

11-3. operatorキーワードの使い方

演算子オーバーロードでは、operatorキーワードを使います。

C#
public static  operator 演算子(引数)
{
// 処理
}

たとえば、+演算子を定義する場合は次のように書きます。

C#
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y);
}

演算子オーバーロードは、通常public staticで定義します。

11-4. 独自クラスで+演算子を定義する例

2次元ベクトルを表すVector構造体を例にします。

C#
public readonly struct Vector
{
public int X { get; }
public int Y { get; }

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

public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y);
}

public override string ToString()
{
return $"({X}, {Y})";
}
}

使い方は次のとおりです。

C#
Vector v1 = new Vector(2, 3);
Vector v2 = new Vector(4, 5);

Vector result = v1 + v2;

Console.WriteLine(result); // (6, 8)

通常のメソッドで書くこともできます。

C#
Vector result = v1.Add(v2);

しかし、ベクトルの加算のように数学的な意味が明確な場合は、+演算子のほうが直感的です。

11-5. 演算子オーバーロードの注意点

演算子オーバーロードは便利ですが、使いすぎるとコードが読みにくくなります。

たとえば、+が「加算」や「結合」を意味するなら自然ですが、データベース保存やファイル削除のような副作用を持たせるのは避けるべきです。

C#
// 悪い例: +で保存処理を行うような設計は直感に反する
user + database;

演算子オーバーロードを使うときは、次の点を意識しましょう。

注意点説明
直感的な意味にする+は加算・結合など自然な意味にする
副作用を避ける演算子で予想外の状態変更をしない
対になる演算子を定義する==!=<>など
Equals/GetHashCodeも考慮する等価比較の整合性を保つ
読みやすさを優先する無理に演算子を使わない

演算子オーバーロードは、独自型を自然に扱うための機能であり、短く書くためだけの機能ではありません。

12. C#演算子でよくあるエラーと対処法

C#演算子は便利ですが、初心者が間違えやすいポイントもあります。ここでは、よくあるエラーと対処法を紹介します。

12-1. =と==を間違える

=は代入、==は比較です。

C#
int x = 10;

if (x == 10)
{
Console.WriteLine("xは10です");
}

bool変数では、誤って代入してしまうケースがあります。

C#
bool isValid = false;

if (isValid = true)
{
Console.WriteLine("常に実行される可能性があります");
}

正しくは次のように書きます。

C#
if (isValid)
{
Console.WriteLine("有効です");
}

否定なら次のようにします。

C#
if (!isValid)
{
Console.WriteLine("無効です");
}

12-2. &&と&を混同する

条件式では基本的に&&を使います。&&は左側がfalseなら右側を評価しません。

C#
string? text = null;

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

一方、&は両方を評価します。

C#
// textがnullの場合、右辺も評価されるため例外になる可能性がある
if (text != null & text.Length > 0)
{
Console.WriteLine(text);
}

nullチェックを含む条件では、&&を使うことが重要です。

12-3. null参照例外が発生する

nullの可能性がある変数に.でアクセスすると、NullReferenceExceptionが発生します。

C#
User? user = null;

// Console.WriteLine(user.Name); // 例外

null条件演算子?.を使えば安全です。

C#
Console.WriteLine(user?.Name);

代替値を表示したい場合は??を組み合わせます。

C#
Console.WriteLine(user?.Name ?? "名前なし");

このパターンは、C#でnull安全なコードを書くときの基本です。

12-4. 整数除算で小数点以下が消える

int同士の除算は、結果もintです。

C#
int total = 5;
int count = 2;

double average = total / count;

Console.WriteLine(average); // 2

averagedoubleでも、右辺のtotal / countが先に整数除算されるため、結果は2になります。

正しくは、どちらかをdoubleに変換します。

C#
double average = (double)total / count;

Console.WriteLine(average); // 2.5

12-5. 優先順位の勘違いで結果が変わる

演算子の優先順位を誤解すると、意図しない結果になります。

C#
int result = 2 + 3 * 4;

Console.WriteLine(result); // 14

20にしたい場合は括弧を使います。

C#
int result = (2 + 3) * 4;

Console.WriteLine(result); // 20

条件式でも同じです。

C#
if (isAdmin || isManager && isActive)
{
Console.WriteLine("許可");
}

意図が曖昧なら、括弧を使いましょう。

C#
if ((isAdmin || isManager) && isActive)
{
Console.WriteLine("許可");
}

13. C#演算子の実践サンプルコード集

ここでは、実務でよくある場面を想定して、C# operatorsの使い方をまとめます。

13-1. 数値計算で使う演算子

商品の合計金額を計算する例です。

C#
decimal price = 1500m;
int quantity = 3;
decimal taxRate = 0.10m;

decimal subtotal = price * quantity;
decimal tax = subtotal * taxRate;
decimal total = subtotal + tax;

Console.WriteLine($"小計: {subtotal}円");
Console.WriteLine($"税額: {tax}円");
Console.WriteLine($"合計: {total}円");

平均点を計算する例です。

C#
int math = 80;
int english = 90;
int science = 75;

double average = (math + english + science) / 3.0;

Console.WriteLine($"平均点: {average}");

剰余演算子で偶数だけ表示する例です。

C#
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
{
Console.WriteLine(i);
}
}

13-2. 条件分岐で使う演算子

年齢と会員状態で割引を判定する例です。

C#
int age = 70;
bool isMember = true;

if (age >= 65 || isMember)
{
Console.WriteLine("割引対象です");
}
else
{
Console.WriteLine("通常料金です");
}

ログイン判定の例です。

C#
string? userId = "user001";
string? password = "secret";
bool isLocked = false;

if (!string.IsNullOrEmpty(userId) &&
!string.IsNullOrEmpty(password) &&
!isLocked)
{
Console.WriteLine("ログイン処理を実行します");
}

三項条件演算子を使う例です。

C#
int score = 58;

string message = score >= 60 ? "合格" : "不合格";

Console.WriteLine(message);

13-3. null対策で使う演算子

null合体演算子で表示名を決める例です。

C#
string? nickname = null;
string? userName = "taro";

string displayName = nickname ?? userName ?? "ゲスト";

Console.WriteLine(displayName); // taro

null合体代入演算子でリストを初期化する例です。

C#
List<string>? messages = null;

messages ??= new List<string>();
messages.Add("Hello");

Console.WriteLine(messages.Count);

null条件演算子でネストしたプロパティにアクセスする例です。

C#
Order? order = null;

string customerName = order?.Customer?.Name ?? "顧客未設定";

Console.WriteLine(customerName);

class Order
{
public Customer? Customer { get; set; }
}

class Customer
{
public string? Name { get; set; }
}

13-4. 配列・文字列操作で使う演算子

配列の要素にアクセスする例です。

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

Console.WriteLine(numbers[0]); // 10

末尾の要素を取得する例です。

C#
Console.WriteLine(numbers[^1]); // 30

範囲を切り出す例です。

C#
int[] selected = numbers[1..];

foreach (int n in selected)
{
Console.WriteLine(n); // 20, 30
}

文字列の一部を取得する例です。

C#
string text = "CSharp";

Console.WriteLine(text[..1]); // C
Console.WriteLine(text[1..]); // Sharp
Console.WriteLine(text[^1]); // p

13-5. LINQやラムダ式で見かける演算子

LINQでは、ラムダ演算子=>をよく使います。

C#
var numbers = new[] { 1, 2, 3, 4, 5, 6 };

var evenNumbers = numbers.Where(x => x % 2 == 0);

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

条件に合う最初の要素を取得する例です。

C#
var names = new[] { "Alice", "Bob", "Charlie" };

string? result = names.FirstOrDefault(name => name.StartsWith("B"));

Console.WriteLine(result); // Bob

射影にも使います。

C#
var users = new[]
{
new { Name = "Alice", Age = 20 },
new { Name = "Bob", Age = 17 }
};

var adultNames = users
.Where(user => user.Age >= 18)
.Select(user => user.Name);

foreach (var name in adultNames)
{
Console.WriteLine(name);
}

LINQを読むには、.(), =>、比較演算子、論理演算子を理解しておくことが大切です。

14. csharp operatorsに関するよくある質問

最後に、csharp operatorsについて初心者が疑問に思いやすいポイントをQ&A形式で解説します。

14-1. C#の演算子は全部覚える必要がある?

最初から全部覚える必要はありません。まずは、使用頻度の高い演算子から覚えれば十分です。

初心者が最初に覚えるべき演算子は次のとおりです。

種類演算子
算術+-*/%
代入=+=-=
比較==!=<><=>=
論理&&、`
null関連???.
型判定is
メンバーアクセス.[]()

ビット演算子や演算子オーバーロードは、必要になったタイミングで学べば問題ありません。

14-2. ==とEqualsの違いは?

==は演算子、Equalsはメソッドです。

C#
string a = "C#";
string b = "C#";

Console.WriteLine(a == b); // True
Console.WriteLine(a.Equals(b)); // True

文字列ではどちらも内容比較として使えます。ただし、クラスによっては==が参照比較、Equalsが内容比較になる場合があります。

C#
object x = new object();
object y = new object();

Console.WriteLine(x == y); // False
Console.WriteLine(x.Equals(y)); // False

独自クラスで等価性を定義する場合は、EqualsGetHashCode、必要に応じて==!=の整合性を考える必要があります。

14-3. &&と||はどちらが先に評価される?

優先順位としては、&&||より先に評価されます。

C#
bool result = true || false && false;

Console.WriteLine(result); // True

これは次のように評価されます。

C#
true || (false && false)

ただし、優先順位に頼りすぎると読みにくくなります。複雑な条件では括弧を使いましょう。

C#
bool result = (true || false) && false;

Console.WriteLine(result); // False

14-4. ??と?:の違いは?

??はnullかどうかで値を選ぶ演算子です。

C#
string? name = null;

string displayName = name ?? "ゲスト";

?:は任意の条件で値を選ぶ演算子です。

C#
int score = 80;

string result = score >= 60 ? "合格" : "不合格";

違いをまとめると次のようになります。

演算子用途
??nullの場合の代替値を指定する
?:条件に応じて2つの値から選ぶ

組み合わせて使うこともあります。

C#
string? name = null;
bool isGuest = true;

string displayName = name ?? (isGuest ? "ゲスト" : "未設定");

14-5. 初心者が最初に覚えるべき演算子は?

初心者は、まず次の演算子を優先して覚えるのがおすすめです。

C#
+  -  *  /  %
= += -=
== != < > <= >=
&& || !
?? ?.
is
. [] ()

これらを理解すれば、基本的なC#コードの多くを読めるようになります。

たとえば、次のコードを理解できれば、基本的な演算子はかなり身についています。

C#
User? user = GetUser();

if (user is not null && user.Age >= 18)
{
string name = user.Name ?? "名前なし";
Console.WriteLine($"{name}さんは成人です");
}

このコードには、is not null&&>=.??、メソッド呼び出し()が含まれています。実務でもよく見る形です。

まとめ

csharp operators、つまりC#演算子は、C#プログラミングの基礎であり、コードを正しく読み書きするために欠かせない知識です。

算術演算子は数値計算に使い、比較演算子と論理演算子は条件分岐に使います。代入演算子と複合代入演算子は変数の更新に使い、????=?.?[]などのnull関連演算子は安全で簡潔なnullチェックに役立ちます。

また、isastypeofsizeofなどの型に関する演算子、&|^<<>>などのビット演算子、nameofawait=>..^といった特殊な演算子も、C#のコードを読むうえで重要です。

特に大切なのは、演算子の意味だけでなく、優先順位と結合規則を理解することです。複雑な式では、無理に暗記へ頼らず、括弧を使って意図を明確にしましょう。

C# operatorsは種類が多く見えますが、最初はよく使うものから覚えれば十分です。+==&&???.=>などを使いこなせるようになると、C#のコード理解と実装スピードは大きく向上します。