C#の比較演算子を完全解説|==・Equals・CompareToの違いと使い分け

はじめに

C#でプログラムを書くとき、「比較」はほぼ必ず登場します。

たとえば、次のような処理です。

C#
if (age >= 20)
{
Console.WriteLine("成人です");
}

if (name == "田中")
{
Console.WriteLine("田中さんです");
}

一見すると単純に見えるC#の比較ですが、実際には次のように複数の方法があります。

C#
a == b
a.Equals(b)
a.CompareTo(b)
string.Compare(a, b)

これらはすべて「比較」に関係しますが、目的や戻り値、使うべき場面が異なります。

この記事では、C#の比較演算子、==EqualsCompareToCompareCompareOrdinalの違いと使い分けを、初心者にもわかりやすく解説します。

1. C#の「比較」でできることを最初に整理

C#の比較を理解するには、まず「何を比べたいのか」を整理することが大切です。

比較といっても、実際には次のような目的があります。

C#
// 等しいかを調べる
x == y

// 大小関係を調べる
x < y

// 並び順を調べる
x.CompareTo(y)

同じ「比較」でも、等しいかを判定したいのか、大小を判定したいのか、ソート順を決めたいのかで使う方法が変わります。

1-1. C#の比較には「等しいか」「大小関係」「並び順」の3種類がある

C#の比較は、大きく分けると次の3種類です。

比較の種類目的
等値比較2つの値が等しいか調べるa == b, a.Equals(b)
大小比較どちらが大きいか、小さいか調べるa < b, a >= b
順序比較並び順として前後関係を調べるa.CompareTo(b)

たとえば、数値であれば次のように比較できます。

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

Console.WriteLine(a == b); // False
Console.WriteLine(a < b); // True

一方、文字列の場合は「等しいか」と「並び順」で使う方法が変わります。

C#
string x = "apple";
string y = "banana";

Console.WriteLine(x == y); // False
Console.WriteLine(x.CompareTo(y)); // 0以外

1-2. 比較演算子・Equals・CompareToの役割の違い

C#の比較でよく使うものは、主に次の3つです。

方法戻り値主な用途
比較演算子bool等しいか、大きいか、小さいかを判定する
Equalsbool値として等しいかを判定する
CompareToint並び順や大小関係を判定する

比較演算子は、条件分岐で使いやすい書き方です。

C#
if (score >= 80)
{
Console.WriteLine("合格");
}

Equalsは、値として等しいかを調べるメソッドです。

C#
if (name.Equals("佐藤"))
{
Console.WriteLine("佐藤さんです");
}

CompareToは、同じかどうかだけでなく、どちらが前か後かを調べます。

C#
int result = "apple".CompareTo("banana");

if (result < 0)
{
Console.WriteLine("apple は banana より前です");
}

1-3. まず押さえたい結論:使い分け早見表

C#の比較では、まず次のように使い分けるとわかりやすいです。

やりたいこと使う方法
数値が等しいか調べたい==
数値の大小を調べたい<, >, <=, >=
文字列が等しいか調べたい== または string.Equals
大文字小文字を無視して文字列比較したいstring.Equals + StringComparison
null安全に比較したいobject.Equals または string.Equals
並び順を調べたいCompareTo または Compare
独自クラスで値として比較したいEqualsをオーバーライドする
独自クラスをソートしたいIComparable<T>を実装する
DictionaryやHashSetで正しく扱いたいEqualsGetHashCodeを実装する

迷った場合は、次の考え方で選ぶとよいです。

C#
// 等しいかだけ知りたい
a == b
a.Equals(b)

// 大小や並び順を知りたい
a.CompareTo(b)

2. C#の比較演算子の基本

C#の比較演算子は、2つの値を比較して結果を返す演算子です。

代表的な比較演算子は次のとおりです。

演算子意味
==等しい
!=等しくない
<より小さい
>より大きい
<=以下
>=以上

比較演算子の結果は、基本的にtrueまたはfalseです。

C#
int x = 10;
int y = 20;

Console.WriteLine(x == y); // False
Console.WriteLine(x != y); // True
Console.WriteLine(x < y); // True
Console.WriteLine(x >= y); // False

2-1. 等値演算子:== と != の使い方

==は、2つの値が等しいかを判定します。

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

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

!=は、2つの値が等しくないかを判定します。

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

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

==!=は、数値だけでなく、boolenumstringなどでもよく使われます。

C#
bool isActive = true;

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

ただし、boolの場合は次のように書く方が自然です。

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

2-2. 関係演算子:<・>・<=・>= の使い方

関係演算子は、大小関係を調べるために使います。

C#
int score = 85;

if (score >= 80)
{
Console.WriteLine("合格です");
}

それぞれの意味は次のとおりです。

C#
10 < 20   // 10は20より小さい
20 > 10 // 20は10より大きい
10 <= 10 // 10は10以下
20 >= 20 // 20は20以上

主に数値の比較で使われます。

C#
decimal price = 1200m;

if (price > 1000m)
{
Console.WriteLine("1000円より高いです");
}

日付の比較にも使えます。

C#
DateTime today = DateTime.Today;
DateTime deadline = new DateTime(2026, 12, 31);

if (today <= deadline)
{
Console.WriteLine("期限内です");
}

2-3. boolを返す比較と、intを返す比較の違い

比較には、boolを返すものとintを返すものがあります。

比較演算子やEqualsは、boolを返します。

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

bool result = a < b;
Console.WriteLine(result); // True

一方、CompareTointを返します。

C#
int result = a.CompareTo(b);
Console.WriteLine(result); // 負の数

CompareToの戻り値は、次のような意味を持ちます。

戻り値意味
0より小さい左側が右側より前、または小さい
0等しい
0より大きい左側が右側より後、または大きい

つまり、CompareToは単に等しいかどうかではなく、大小や並び順まで表現できます。

2-4. if文・while文・三項演算子での比較の使い方

比較は、条件分岐でよく使います。

C#
int age = 18;

if (age >= 18)
{
Console.WriteLine("利用できます");
}
else
{
Console.WriteLine("利用できません");
}

while文でも使えます。

C#
int count = 0;

while (count < 5)
{
Console.WriteLine(count);
count++;
}

三項演算子でも比較結果を使えます。

C#
int score = 75;

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

Console.WriteLine(result);

比較式はtrueまたはfalseになるため、条件式としてそのまま使えます。

2-5. 比較演算子でよくあるコンパイルエラー

C#では、型によって使える比較演算子が決まっています。

たとえば、文字列に対して<>を使うとコンパイルエラーになります。

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

// エラー
// if (a < b)
// {
// }

文字列の並び順を比較したい場合は、CompareTostring.Compareを使います。

C#
if (a.CompareTo(b) < 0)
{
Console.WriteLine("aはbより前です");
}

また、異なる型同士をそのまま比較しようとしてエラーになることもあります。

C#
int number = 10;
string text = "10";

// エラー
// Console.WriteLine(number == text);

比較する前に、型をそろえる必要があります。

C#
Console.WriteLine(number.ToString() == text); // True

3. ==演算子の使い方と注意点

==演算子は、C#で最もよく使われる比較方法のひとつです。

ただし、型によって比較の意味が変わるため注意が必要です。

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

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

数値型では値を比較しますが、参照型では「同じインスタンスか」を比較する場合があります。

3-1. 数値型・bool型・enum型を==で比較する

数値型では、==は値が等しいかを比較します。

C#
int x = 100;
int y = 100;

Console.WriteLine(x == y); // True

doubledecimalでも使えます。

C#
decimal price1 = 1000m;
decimal price2 = 1000m;

Console.WriteLine(price1 == price2); // True

bool型でも比較できます。

C#
bool a = true;
bool b = false;

Console.WriteLine(a == b); // False

enum型でも==を使えます。

C#
enum Status
{
Active,
Inactive
}

Status status = Status.Active;

if (status == Status.Active)
{
Console.WriteLine("有効です");
}

3-2. string型を==で比較する

C#のstring型では、==で文字列の内容を比較できます。

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

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

stringは参照型ですが、==演算子がオーバーロードされているため、通常は文字列の内容で比較されます。

C#
string x = new string(new[] { 'a', 'b', 'c' });
string y = new string(new[] { 'a', 'b', 'c' });

Console.WriteLine(x == y); // True

これは、xyが別々のインスタンスでも、文字列の内容が同じならtrueになるということです。

ただし、大文字と小文字は区別されます。

C#
Console.WriteLine("abc" == "ABC"); // False

3-3. nullとの比較で==を使う

nullかどうかを調べるときにも、==はよく使われます。

C#
string? name = null;

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

nullではないことを調べる場合は、!=を使います。

C#
if (name != null)
{
Console.WriteLine(name.Length);
}

最近のC#では、パターンマッチングを使って次のようにも書けます。

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

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

is nullは、演算子オーバーロードの影響を受けにくいため、null判定を明確に書きたい場合に便利です。

3-4. 参照型での==は「同じインスタンスか」を見る場合がある

通常のクラスでは、==は参照が同じかどうかを比較します。

C#
class Person
{
public string Name { get; set; } = "";
}

Person p1 = new Person { Name = "田中" };
Person p2 = new Person { Name = "田中" };

Console.WriteLine(p1 == p2); // False

p1p2は、どちらもName"田中"ですが、別々に作られたインスタンスです。

そのため、==の結果はfalseになります。

C#
Person p3 = p1;

Console.WriteLine(p1 == p3); // True

p3p1と同じインスタンスを指しているため、trueになります。

3-5. ==演算子がオーバーロードされている型に注意する

C#では、型によって==演算子の動作を独自に定義できます。

これを演算子のオーバーロードといいます。

代表的な例がstringです。

C#
string a = new string(new[] { 'x' });
string b = new string(new[] { 'x' });

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

stringでは、参照ではなく文字列の内容で比較されます。

また、自作クラスでも==をオーバーロードできます。

C#
public static bool operator ==(Person? left, Person? right)
{
return Equals(left, right);
}

ただし、==をオーバーロードする場合は、!=EqualsGetHashCodeとの整合性を保つ必要があります。

4. Equalsメソッドの使い方と==との違い

Equalsは、値として等しいかを判定するためのメソッドです。

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

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

==と似ていますが、Equalsはメソッドであり、型によって実装を変更できます。

また、object.Equalsを使うと、nullに対して比較しやすくなります。

C#
Console.WriteLine(object.Equals(a, b)); // True

4-1. Equalsは「値として等しいか」を判定するためのメソッド

Equalsは、2つのオブジェクトが値として等しいかを判定するために使われます。

C#
int x = 10;
int y = 10;

Console.WriteLine(x.Equals(y)); // True

文字列でも使えます。

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

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

ただし、独自クラスでEqualsをオーバーライドしていない場合、既定では参照の比較になることがあります。

C#
class Product
{
public int Id { get; set; }
}

Product p1 = new Product { Id = 1 };
Product p2 = new Product { Id = 1 };

Console.WriteLine(p1.Equals(p2)); // False

Idが同じでも、別インスタンスなのでfalseになります。

4-2. object.EqualsとインスタンスのEqualsの違い

Equalsには、主に次の2つの使い方があります。

C#
a.Equals(b)
object.Equals(a, b)

インスタンスメソッドのEqualsは、左側がnullの場合に例外になります。

C#
string? a = null;
string b = "test";

// 例外
// Console.WriteLine(a.Equals(b));

一方、object.Equalsはnull安全です。

C#
string? a = null;
string b = "test";

Console.WriteLine(object.Equals(a, b)); // False

両方がnullならtrueになります。

C#
string? x = null;
string? y = null;

Console.WriteLine(object.Equals(x, y)); // True

nullの可能性がある値を比較する場合は、object.Equalsが便利です。

4-3. string.Equalsで文字列を比較する

文字列比較では、string.Equalsを使うと比較方法を明示できます。

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

Console.WriteLine(string.Equals(a, b)); // False

大文字小文字を無視したい場合は、StringComparisonを指定します。

C#
Console.WriteLine(
string.Equals(a, b, StringComparison.OrdinalIgnoreCase)
); // True

文字列比較では、StringComparisonを明示すると意図が伝わりやすくなります。

C#
string.Equals(a, b, StringComparison.Ordinal)
string.Equals(a, b, StringComparison.OrdinalIgnoreCase)
string.Equals(a, b, StringComparison.CurrentCulture)
string.Equals(a, b, StringComparison.CurrentCultureIgnoreCase)

特に、ID、コード、キー、ファイル名、内部的な識別子などを比較する場合は、Ordinal系を使うことが多いです。

4-4. nullに対してEqualsを呼ぶと例外になるケース

Equalsでよくあるミスが、nullに対してインスタンスメソッドを呼んでしまうことです。

C#
string? name = null;

// NullReferenceException
// if (name.Equals("田中"))
// {
// }

この場合、namenullなので、Equalsメソッドを呼び出せません。

安全に書くには、次のようにします。

C#
if (string.Equals(name, "田中", StringComparison.Ordinal))
{
Console.WriteLine("田中さんです");
}

または、比較対象がnullでない定数なら、定数側から呼ぶ方法もあります。

C#
if ("田中".Equals(name))
{
Console.WriteLine("田中さんです");
}

ただし、文字列比較ではStringComparisonを明示できるstring.Equalsの方がより安全です。

4-5. ==とEqualsの結果が異なるケース

==Equalsは、型によって結果が異なることがあります。

たとえば、通常のクラスではどちらも参照比較になることが多いです。

C#
class User
{
public int Id { get; set; }
}

User u1 = new User { Id = 1 };
User u2 = new User { Id = 1 };

Console.WriteLine(u1 == u2); // False
Console.WriteLine(u1.Equals(u2)); // False

一方、Equalsだけをオーバーライドすると、結果が変わります。

C#
class User
{
public int Id { get; set; }

public override bool Equals(object? obj)
{
return obj is User other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

この場合、Equalsでは値として比較されます。

C#
User u1 = new User { Id = 1 };
User u2 = new User { Id = 1 };

Console.WriteLine(u1 == u2); // False
Console.WriteLine(u1.Equals(u2)); // True

==はオーバーロードしていないため参照比較、EqualsIdで比較するためtrueになります。

4-6. 独自クラスでEqualsを使うときの注意点

独自クラスで値として等しいかを判定したい場合は、Equalsをオーバーライドします。

C#
class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";

public override bool Equals(object? obj)
{
return obj is Product other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

このようにすると、Idが同じなら同じ商品として扱えます。

C#
Product p1 = new Product { Id = 1, Name = "PC" };
Product p2 = new Product { Id = 1, Name = "Laptop" };

Console.WriteLine(p1.Equals(p2)); // True

注意点は、Equalsをオーバーライドしたら、原則としてGetHashCodeも一緒にオーバーライドすることです。

DictionaryHashSetでは、EqualsだけでなくGetHashCodeも使われるためです。

5. CompareTo・Compare・CompareOrdinalの違い

CompareToCompareCompareOrdinalは、等しいかどうかだけでなく、大小関係や並び順を比較するために使います。

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

int result = a.CompareTo(b);

戻り値はintです。

C#
if (result < 0)
{
Console.WriteLine("aはbより前です");
}

5-1. CompareToは大小関係や並び順を比較するメソッド

CompareToは、現在の値と指定した値を比較します。

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

Console.WriteLine(a.CompareTo(b)); // 負の数
Console.WriteLine(b.CompareTo(a)); // 正の数
Console.WriteLine(a.CompareTo(10)); // 0

文字列にも使えます。

C#
string x = "apple";
string y = "banana";

Console.WriteLine(x.CompareTo(y)); // 負の数

CompareToは、ソート処理でもよく使われます。

C#
List<int> numbers = new List<int> { 3, 1, 2 };
numbers.Sort();

Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 3

5-2. CompareToの戻り値:0・正の数・負の数の意味

CompareToの戻り値は、次のような意味です。

戻り値意味
負の数自分の方が小さい、または前に来る
0等しい
正の数自分の方が大きい、または後に来る

重要なのは、具体的な数値そのものではなく、符号を見ることです。

C#
int result = "apple".CompareTo("banana");

if (result < 0)
{
Console.WriteLine("appleはbananaより前です");
}
else if (result == 0)
{
Console.WriteLine("同じです");
}
else
{
Console.WriteLine("appleはbananaより後です");
}

次のように、1-1と決め打ちしない方が安全です。

C#
// あまりよくない
if (result == -1)
{
}

正しくは、0より小さいか0か0より大きいかで判定します。

C#
if (result < 0)
{
}

5-3. string.Compareとの違い

string.Compareは、2つの文字列を比較する静的メソッドです。

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

int result = string.Compare(a, b);

CompareToはインスタンスメソッドです。

C#
int result = a.CompareTo(b);

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

方法呼び出し方特徴
CompareToa.CompareTo(b)対象のインスタンスから呼ぶ
string.Comparestring.Compare(a, b)静的メソッドで比較する
string.CompareOrdinalstring.CompareOrdinal(a, b)序数比較を行う

string.Compareは、比較方法を指定できる点が便利です。

C#
int result = string.Compare(
"abc",
"ABC",
StringComparison.OrdinalIgnoreCase
);

Console.WriteLine(result); // 0

5-4. string.CompareOrdinalとの違い

string.CompareOrdinalは、文字列を序数比較するためのメソッドです。

C#
int result = string.CompareOrdinal("abc", "ABC");

序数比較とは、言語や文化に依存せず、文字のコード値に基づいて比較する方法です。

内部的なキー、ID、コード、プロトコル、ファイルパスの一部など、言語的な並び順ではなく機械的な比較をしたい場合に向いています。

C#
string id1 = "user001";
string id2 = "user002";

int result = string.CompareOrdinal(id1, id2);

人間向けの表示順を考慮したい場合はカルチャ依存の比較、システム内部の識別子を比較したい場合はOrdinal比較を選ぶのが基本です。

5-5. EqualsではなくCompareToを使うべき場面

Equalsは、等しいかどうかだけを判定します。

C#
a.Equals(b)

一方、CompareToは、どちらが前か後かも判定できます。

C#
a.CompareTo(b)

そのため、次のような場面ではCompareToが向いています。

C#
// 並び順を判定したい
if (a.CompareTo(b) < 0)
{
}

// ソート順を決めたい
items.Sort();

// 範囲判定に使いたい
if (value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0)
{
}

等しいかだけ知りたいならEquals、順序まで知りたいならCompareToと覚えるとわかりやすいです。

5-6. ソート処理でCompareToが使われる仕組み

List<T>.Sort()は、要素の並び替えに比較ロジックを使います。

たとえば、intstringは既に比較方法を持っているため、そのままソートできます。

C#
List<string> names = new List<string>
{
"banana",
"apple",
"orange"
};

names.Sort();

Console.WriteLine(string.Join(", ", names));
// apple, banana, orange

独自クラスをソートしたい場合は、IComparable<T>を実装してCompareToを定義できます。

C#
class Product : IComparable<Product>
{
public int Price { get; set; }

public int CompareTo(Product? other)
{
if (other is null) return 1;
return Price.CompareTo(other.Price);
}
}

これにより、Productのリストを価格順に並び替えられます。

C#
products.Sort();

6. 文字列比較で失敗しないための実践知識

C#の比較で特に注意が必要なのが文字列比較です。

文字列は、単純に見えて次のような違いがあります。

C#
"abc"  "ABC" を同じと見るか
日本語や記号をどう並べるか
カルチャを考慮するか
システム内部の文字列として比較するか

文字列比較では、StringComparisonを明示することで意図しない挙動を避けやすくなります。

6-1. 大文字・小文字を区別する比較

通常の==Equalsでは、大文字と小文字は区別されます。

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

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

明示的に書くなら、次のようにします。

C#
bool result = string.Equals(
a,
b,
StringComparison.Ordinal
);

Console.WriteLine(result); // False

StringComparison.Ordinalは、言語や文化に依存しない比較です。

6-2. 大文字・小文字を区別しない比較

大文字と小文字を区別しない場合は、StringComparison.OrdinalIgnoreCaseを使います。

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

bool result = string.Equals(
a,
b,
StringComparison.OrdinalIgnoreCase
);

Console.WriteLine(result); // True

ログインID、コマンド、拡張子、キーなどで大文字小文字を無視したい場合によく使います。

C#
string input = "YES";

if (string.Equals(input, "yes", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("はい");
}

6-3. カルチャ依存の比較とOrdinal比較の違い

文字列比較には、大きく分けてカルチャ依存の比較とOrdinal比較があります。

比較方法特徴主な用途
カルチャ依存言語や文化のルールを考慮する人間向けの表示、並び順
Ordinal文字コードに基づく機械的な比較ID、キー、内部処理

カルチャ依存の比較は、ユーザーに見せる文字列の並び替えなどに向いています。

C#
string.Compare(a, b, StringComparison.CurrentCulture)

Ordinal比較は、システム内部の安定した比較に向いています。

C#
string.Equals(a, b, StringComparison.Ordinal)
string.CompareOrdinal(a, b)

たとえば、商品名や人名を画面に表示する順番で並べるならカルチャを考慮する場合があります。

一方、設定キーや識別子を比較するならOrdinal比較が適しています。

6-4. 日本語・英語・記号を含む文字列比較の注意点

日本語、英語、記号が混在する文字列では、比較方法によって結果が変わることがあります。

C#
List<string> words = new List<string>
{
"apple",
"アップル",
"Apple",
"#tag"
};

このような文字列を並び替える場合、単にSort()するだけでは、期待する表示順と異なることがあります。

C#
words.Sort();

人間に自然な順番で見せたいのか、システム的に安定した順番にしたいのかを考える必要があります。

C#
words.Sort((x, y) => string.Compare(
x,
y,
StringComparison.CurrentCulture
));

システム的な比較なら、次のようにOrdinalを使います。

C#
words.Sort((x, y) => string.Compare(
x,
y,
StringComparison.Ordinal
));

6-5. 文字列比較ではStringComparisonを明示するのが安全

文字列比較では、できるだけStringComparisonを明示するのが安全です。

C#
string.Equals(a, b, StringComparison.Ordinal)

大文字小文字を無視するなら、次のようにします。

C#
string.Equals(a, b, StringComparison.OrdinalIgnoreCase)

並び順を比較するなら、次のようにします。

C#
string.Compare(a, b, StringComparison.Ordinal)

StringComparisonを明示すると、コードを読む人に「どのルールで比較しているか」が伝わります。

特に、次のような文字列では明示することをおすすめします。

C#
ユーザーID
メールアドレス
ファイル拡張子
設定キー
URL
コマンド名
コード値

6-6. パフォーマンスを意識した文字列比較

文字列比較では、不要な変換を避けることも大切です。

たとえば、大文字小文字を無視したいからといって、毎回ToLower()して比較するのはおすすめしません。

C#
// あまりよくない
if (a.ToLower() == b.ToLower())
{
}

代わりに、StringComparison.OrdinalIgnoreCaseを使います。

C#
if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
{
}

この方が意図が明確で、余計な文字列生成も避けやすくなります。

大量の文字列を比較する処理では、比較方法を明示することが可読性とパフォーマンスの両面で重要です。

7. 値型・参照型・recordで比較結果が変わる理由

C#の比較では、値型、参照型、recordで結果が変わることがあります。

これは、C#の型が「値そのもの」を扱うのか、「オブジェクトへの参照」を扱うのかによって比較の意味が変わるためです。

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

Console.WriteLine(a == b); // True
C#
Person p1 = new Person { Name = "田中" };
Person p2 = new Person { Name = "田中" };

Console.WriteLine(p1 == p2); // False

この違いを理解すると、C#の比較で混乱しにくくなります。

7-1. 値型の比較は基本的に「値の比較」

intdoubleboolDateTimestructなどは値型です。

値型では、基本的に値そのものが比較されます。

C#
int x = 100;
int y = 100;

Console.WriteLine(x == y); // True

DateTimeでも値として比較できます。

C#
DateTime d1 = new DateTime(2026, 1, 1);
DateTime d2 = new DateTime(2026, 1, 1);

Console.WriteLine(d1 == d2); // True

ただし、独自のstructで比較をどう扱うかは、実装によって注意が必要です。

7-2. 参照型の比較は「参照の比較」になることがある

クラスは参照型です。

参照型では、==が「同じオブジェクトを指しているか」を比較することがあります。

C#
class Customer
{
public string Name { get; set; } = "";
}

Customer c1 = new Customer { Name = "佐藤" };
Customer c2 = new Customer { Name = "佐藤" };

Console.WriteLine(c1 == c2); // False

Nameの値は同じですが、c1c2は別のインスタンスです。

同じ参照を代入した場合はtrueになります。

C#
Customer c3 = c1;

Console.WriteLine(c1 == c3); // True

値として比較したい場合は、Equalsをオーバーライドするか、recordを使う方法があります。

7-3. stringは参照型でも値のように比較できる

stringは参照型ですが、==で文字列の内容を比較できます。

C#
string a = new string(new[] { 'A' });
string b = new string(new[] { 'A' });

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

これは、string==演算子をオーバーロードしているためです。

そのため、C#では文字列比較に==を使っても、多くの場合は問題ありません。

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

ただし、大文字小文字の扱いやカルチャを明確にしたい場合は、string.EqualsStringComparisonを指定する方が安全です。

C#
if (string.Equals(name, "admin", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("管理者です");
}

7-4. record型は値ベースの比較ができる

recordは、値ベースの比較を簡単に実現できる型です。

C#
public record Person(string Name, int Age);

同じ値を持つrecord同士は、等しいと判定されます。

C#
var p1 = new Person("田中", 30);
var p2 = new Person("田中", 30);

Console.WriteLine(p1 == p2); // True
Console.WriteLine(p1.Equals(p2)); // True

通常のclassでは別インスタンスならfalseになることが多いですが、recordでは値の内容で比較されます。

データを表す型を作る場合、recordは非常に便利です。

7-5. struct・class・recordの比較の違い

structclassrecordの比較の違いを整理すると次のようになります。

特徴比較の傾向
struct値型値として比較されやすい
class参照型参照比較になることが多い
record値ベースの型値ベースで比較される

例を見てみましょう。

C#
public class ClassPerson
{
public string Name { get; set; } = "";
}

public record RecordPerson(string Name);
C#
var c1 = new ClassPerson { Name = "田中" };
var c2 = new ClassPerson { Name = "田中" };

Console.WriteLine(c1 == c2); // False

var r1 = new RecordPerson("田中");
var r2 = new RecordPerson("田中");

Console.WriteLine(r1 == r2); // True

データの同値性を重視するなら、recordを選ぶと実装量を減らせます。

8. 独自クラスで比較を正しく実装する方法

独自クラスで「同じ値なら同じものとして扱いたい」場合は、比較の実装が必要です。

たとえば、次のような商品クラスがあるとします。

C#
class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

このままだと、Idが同じでも別インスタンスなら等しいとは判定されません。

C#
var p1 = new Product { Id = 1, Name = "PC" };
var p2 = new Product { Id = 1, Name = "PC" };

Console.WriteLine(p1.Equals(p2)); // False

値として比較したい場合は、EqualsGetHashCodeを実装します。

8-1. Equalsをオーバーライドする基本形

独自クラスでEqualsをオーバーライドする基本形は次のとおりです。

C#
class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";

public override bool Equals(object? obj)
{
if (obj is not Product other)
{
return false;
}

return Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

この例では、Idが同じなら同じ商品として扱います。

C#
var p1 = new Product { Id = 1, Name = "PC" };
var p2 = new Product { Id = 1, Name = "Laptop" };

Console.WriteLine(p1.Equals(p2)); // True

比較対象にどのプロパティを使うかは、クラスの意味によって決めます。

8-2. GetHashCodeも一緒に実装すべき理由

Equalsをオーバーライドしたら、GetHashCodeも一緒に実装する必要があります。

理由は、DictionaryHashSetがハッシュ値を使って要素を管理するためです。

C#
var set = new HashSet<Product>();

set.Add(new Product { Id = 1, Name = "PC" });
set.Add(new Product { Id = 1, Name = "Laptop" });

Console.WriteLine(set.Count);

EqualsGetHashCodeの整合性が取れていれば、同じIdのデータを重複として扱えます。

複数のプロパティを使う場合は、HashCode.Combineが便利です。

C#
public override int GetHashCode()
{
return HashCode.Combine(Id, Name);
}

ただし、Equalsで使うプロパティとGetHashCodeで使うプロパティは、基本的にそろえる必要があります。

8-3. ==演算子と!=演算子をオーバーロードする方法

独自クラスで==!=を使いたい場合は、演算子をオーバーロードします。

C#
class Product
{
public int Id { get; set; }

public override bool Equals(object? obj)
{
return obj is Product other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}

public static bool operator ==(Product? left, Product? right)
{
return Equals(left, right);
}

public static bool operator !=(Product? left, Product? right)
{
return !Equals(left, right);
}
}

これで、==でも値として比較できます。

C#
var p1 = new Product { Id = 1 };
var p2 = new Product { Id = 1 };

Console.WriteLine(p1 == p2); // True

==をオーバーロードする場合は、必ず!=も一緒に実装しましょう。

また、EqualsGetHashCode==!=の結果に矛盾がないようにすることが重要です。

8-4. IEquatable<T>を実装するメリット

IEquatable<T>を実装すると、型安全で効率的な等値比較ができます。

C#
class Product : IEquatable<Product>
{
public int Id { get; set; }

public bool Equals(Product? other)
{
if (other is null)
{
return false;
}

return Id == other.Id;
}

public override bool Equals(object? obj)
{
return Equals(obj as Product);
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

IEquatable<T>を使うと、objectへの変換を避けやすくなり、コレクションでも効率よく比較できます。

C#
var list = new List<Product>
{
new Product { Id = 1 }
};

bool exists = list.Contains(new Product { Id = 1 });

Console.WriteLine(exists); // True

独自型で等値比較をきちんと扱いたい場合は、IEquatable<T>の実装を検討するとよいです。

8-5. IComparable<T>を実装してCompareToを定義する方法

独自クラスを並び替えたい場合は、IComparable<T>を実装します。

C#
class Product : IComparable<Product>
{
public string Name { get; set; } = "";
public int Price { get; set; }

public int CompareTo(Product? other)
{
if (other is null)
{
return 1;
}

return Price.CompareTo(other.Price);
}
}

この例では、価格が安い順に並ぶようにしています。

C#
var products = new List<Product>
{
new Product { Name = "A", Price = 300 },
new Product { Name = "B", Price = 100 },
new Product { Name = "C", Price = 200 }
};

products.Sort();

foreach (var product in products)
{
Console.WriteLine($"{product.Name}: {product.Price}");
}

出力は次のようになります。

C#
B: 100
C: 200
A: 300

複数条件で比較したい場合は、次のように書けます。

C#
public int CompareTo(Product? other)
{
if (other is null) return 1;

int priceResult = Price.CompareTo(other.Price);

if (priceResult != 0)
{
return priceResult;
}

return string.Compare(Name, other.Name, StringComparison.Ordinal);
}

8-6. Dictionary・HashSet・LINQで比較が使われる場面

C#の比較は、さまざまな場面で内部的に使われています。

たとえば、HashSet<T>では重複判定にEqualsGetHashCodeが使われます。

C#
var set = new HashSet<Product>();

set.Add(new Product { Id = 1 });
set.Add(new Product { Id = 1 });

Console.WriteLine(set.Count);

Dictionary<TKey, TValue>では、キーの比較に使われます。

C#
var dict = new Dictionary<Product, string>();

var key1 = new Product { Id = 1 };
var key2 = new Product { Id = 1 };

dict[key1] = "商品A";

Console.WriteLine(dict.ContainsKey(key2));

EqualsGetHashCodeを正しく実装していないと、意図した結果にならないことがあります。

LINQでも比較はよく使われます。

C#
var result = products
.Where(p => p.Price >= 1000)
.OrderBy(p => p.Price)
.ToList();

Whereでは条件比較、OrderByでは並び順の比較が使われています。

9. C#の比較でよくあるミスと対処法

C#の比較では、初心者がつまずきやすいポイントがあります。

代表的なミスは次のとおりです。

C#
nullに対してEqualsを呼ぶ
浮動小数点数を==で比較する
文字列に<>を使う
参照型を==で比較する
Equalsだけ実装してGetHashCodeを忘れる
CompareToの戻り値をtrue/falseのように扱う

それぞれの対処法を見ていきましょう。

9-1. null.EqualsでNullReferenceExceptionが発生する

次のコードは、namenullの場合に例外になります。

C#
string? name = null;

// NullReferenceException
// if (name.Equals("admin"))
// {
// }

安全に比較するには、string.Equalsを使います。

C#
if (string.Equals(name, "admin", StringComparison.Ordinal))
{
Console.WriteLine("adminです");
}

または、object.Equalsを使います。

C#
if (object.Equals(name, "admin"))
{
Console.WriteLine("adminです");
}

定数側からEqualsを呼ぶ方法もあります。

C#
if ("admin".Equals(name))
{
Console.WriteLine("adminです");
}

ただし、文字列比較ではStringComparisonを指定できる書き方がよりおすすめです。

9-2. 浮動小数点数の比較で期待通りにならない

doublefloatでは、計算誤差により==で期待通りにならないことがあります。

C#
double x = 0.1 + 0.2;
double y = 0.3;

Console.WriteLine(x == y); // Falseになることがある

浮動小数点数を比較する場合は、許容誤差を使うことがあります。

C#
double tolerance = 0.000001;

if (Math.Abs(x - y) < tolerance)
{
Console.WriteLine("ほぼ等しい");
}

金額のように正確な小数計算が必要な場合は、decimalを使うことを検討します。

C#
decimal price1 = 0.1m + 0.2m;
decimal price2 = 0.3m;

Console.WriteLine(price1 == price2); // True

9-3. 文字列の大小比較に<や>を使おうとしてエラーになる

C#では、文字列に対して<>を直接使うことはできません。

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

// コンパイルエラー
// if (a < b)
// {
// }

文字列の並び順を比較したい場合は、CompareTostring.Compareを使います。

C#
if (string.Compare(a, b, StringComparison.Ordinal) < 0)
{
Console.WriteLine("aはbより前です");
}

または、次のようにも書けます。

C#
if (a.CompareTo(b) < 0)
{
Console.WriteLine("aはbより前です");
}

ただし、文字列比較ではStringComparisonを明示できるstring.Compareの方が意図を表しやすいです。

9-4. 参照型を==で比較して意図しない結果になる

独自クラスを==で比較すると、値が同じでもfalseになることがあります。

C#
class User
{
public int Id { get; set; }
}

var u1 = new User { Id = 1 };
var u2 = new User { Id = 1 };

Console.WriteLine(u1 == u2); // False

これは、u1u2が別々のインスタンスだからです。

値として比較したい場合は、Equalsをオーバーライドするか、recordを使います。

C#
public record User(int Id);
C#
var u1 = new User(1);
var u2 = new User(1);

Console.WriteLine(u1 == u2); // True

データ中心の型であれば、recordを使うと比較の実装が簡単になります。

9-5. Equalsだけ実装してGetHashCodeを実装し忘れる

Equalsだけを実装してGetHashCodeを実装しないと、HashSetDictionaryで問題が起きることがあります。

C#
class User
{
public int Id { get; set; }

public override bool Equals(object? obj)
{
return obj is User other && Id == other.Id;
}

// GetHashCodeがない
}

正しくは、次のようにGetHashCodeも実装します。

C#
public override int GetHashCode()
{
return Id.GetHashCode();
}

複数の値で比較する場合は、HashCode.Combineを使います。

C#
public override int GetHashCode()
{
return HashCode.Combine(Id, Name);
}

Equalsで等しいと判断されるオブジェクトは、同じハッシュコードを返す必要があります。

9-6. CompareToの戻り値をtrue・falseのように扱ってしまう

CompareToboolではなくintを返します。

C#
int result = a.CompareTo(b);

そのため、次のようには書けません。

C#
// エラー
// if (a.CompareTo(b))
// {
// }

正しくは、戻り値を0と比較します。

C#
if (a.CompareTo(b) == 0)
{
Console.WriteLine("等しい");
}

if (a.CompareTo(b) < 0)
{
Console.WriteLine("aの方が小さい");
}

if (a.CompareTo(b) > 0)
{
Console.WriteLine("aの方が大きい");
}

CompareToは、戻り値の符号で判断するのが基本です。

10. 比較方法の選び方

C#の比較では、目的に応じて使う方法を選ぶことが大切です。

単に等しいか知りたいのか、大小関係を知りたいのか、ソートしたいのかで使う方法が変わります。

C#
// 等しいか
a == b
a.Equals(b)

// 大小
a < b
a > b

// 並び順
a.CompareTo(b)

10-1. 単純な数値比較なら比較演算子を使う

数値の比較なら、基本的に比較演算子を使えば十分です。

C#
int score = 80;

if (score >= 60)
{
Console.WriteLine("合格");
}

等しいかを調べる場合は==を使います。

C#
if (score == 100)
{
Console.WriteLine("満点");
}

範囲判定も比較演算子で書けます。

C#
if (score >= 0 && score <= 100)
{
Console.WriteLine("有効な点数です");
}

10-2. 文字列が等しいか調べるなら==またはstring.Equalsを使う

文字列が等しいかを調べるだけなら、==を使えます。

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

ただし、比較ルールを明確にしたい場合は、string.Equalsを使うのがおすすめです。

C#
if (string.Equals(name, "admin", StringComparison.Ordinal))
{
Console.WriteLine("管理者です");
}

大文字小文字を無視する場合は、次のようにします。

C#
if (string.Equals(name, "admin", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("管理者です");
}

10-3. 文字列の並び順を比較するならCompareToやCompareを使う

文字列の並び順を比較するなら、CompareTostring.Compareを使います。

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

if (a.CompareTo(b) < 0)
{
Console.WriteLine("aはbより前です");
}

比較ルールを明示したい場合は、string.Compareを使います。

C#
if (string.Compare(a, b, StringComparison.Ordinal) < 0)
{
Console.WriteLine("aはbより前です");
}

文字列の大小比較に<>は使えないため注意しましょう。

10-4. null安全に比較したい場合の書き方

nullの可能性がある値を比較する場合は、null安全な書き方を選びます。

文字列なら、string.Equalsが便利です。

C#
string? name = null;

if (string.Equals(name, "admin", StringComparison.Ordinal))
{
Console.WriteLine("adminです");
}

オブジェクト全般では、object.Equalsが使えます。

C#
object? a = null;
object? b = "test";

Console.WriteLine(object.Equals(a, b)); // False

null判定そのものなら、is nullis not nullも読みやすいです。

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

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

10-5. 独自型の同値性を比較したい場合の書き方

独自型を値として比較したい場合は、EqualsGetHashCodeを実装します。

C#
class User : IEquatable<User>
{
public int Id { get; set; }

public bool Equals(User? other)
{
return other is not null && Id == other.Id;
}

public override bool Equals(object? obj)
{
return Equals(obj as User);
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

これにより、ContainsHashSetなどでも意図した比較が行われます。

C#
var users = new List<User>
{
new User { Id = 1 }
};

Console.WriteLine(users.Contains(new User { Id = 1 })); // True

単純なデータ型なら、recordを使うのも有効です。

C#
public record User(int Id, string Name);

10-6. ソートしたい場合の比較方法

数値や文字列のリストは、そのままソートできます。

C#
var numbers = new List<int> { 3, 1, 2 };
numbers.Sort();

Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 3

独自クラスを特定のプロパティで並び替えるなら、LINQのOrderByが簡単です。

C#
var sorted = products
.OrderBy(p => p.Price)
.ToList();

降順にするなら、OrderByDescendingを使います。

C#
var sorted = products
.OrderByDescending(p => p.Price)
.ToList();

型自体に標準の並び順を持たせたい場合は、IComparable<T>を実装します。

C#
class Product : IComparable<Product>
{
public int Price { get; set; }

public int CompareTo(Product? other)
{
if (other is null) return 1;
return Price.CompareTo(other.Price);
}
}

11. 実践コードで理解するC#の比較

ここからは、実際のコードでC#の比較を確認していきます。

数値比較、文字列比較、EqualsCompareTo、独自クラスの比較、ソート処理まで、基本的なパターンをまとめます。

11-1. 数値を比較するサンプルコード

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

Console.WriteLine(a == b); // False
Console.WriteLine(a != b); // True
Console.WriteLine(a < b); // True
Console.WriteLine(a > b); // False
Console.WriteLine(a <= b); // True
Console.WriteLine(a >= b); // False

条件分岐で使う例です。

C#
int score = 75;

if (score >= 60)
{
Console.WriteLine("合格");
}
else
{
Console.WriteLine("不合格");
}

範囲判定の例です。

C#
if (score >= 0 && score <= 100)
{
Console.WriteLine("有効な点数です");
}

11-2. 文字列を==で比較するサンプルコード

C#
string userName = "admin";

if (userName == "admin")
{
Console.WriteLine("管理者です");
}
else
{
Console.WriteLine("一般ユーザーです");
}

stringでは、==で文字列の内容を比較できます。

C#
string a = new string(new[] { 'C', '#'});
string b = new string(new[] { 'C', '#'});

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

大文字小文字は区別されます。

C#
Console.WriteLine("admin" == "ADMIN"); // False

11-3. Equalsで比較するサンプルコード

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

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

null安全に比較するなら、string.Equalsを使います。

C#
string? input = null;

bool result = string.Equals(
input,
"yes",
StringComparison.OrdinalIgnoreCase
);

Console.WriteLine(result); // False

大文字小文字を無視する比較です。

C#
string answer = "YES";

if (string.Equals(answer, "yes", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("はい");
}

11-4. CompareToで並び順を判定するサンプルコード

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

int result = a.CompareTo(b);

if (result < 0)
{
Console.WriteLine("appleはbananaより前です");
}
else if (result == 0)
{
Console.WriteLine("同じです");
}
else
{
Console.WriteLine("appleはbananaより後です");
}

数値のCompareToも同じ考え方です。

C#
int x = 10;
int y = 20;

int result = x.CompareTo(y);

Console.WriteLine(result < 0); // True

CompareToは、戻り値の符号で判定します。

11-5. 独自クラスの比較を実装するサンプルコード

C#
class User : IEquatable<User>
{
public int Id { get; set; }
public string Name { get; set; } = "";

public bool Equals(User? other)
{
if (other is null)
{
return false;
}

return Id == other.Id;
}

public override bool Equals(object? obj)
{
return Equals(obj as User);
}

public override int GetHashCode()
{
return Id.GetHashCode();
}

public static bool operator ==(User? left, User? right)
{
return Equals(left, right);
}

public static bool operator !=(User? left, User? right)
{
return !Equals(left, right);
}
}

使用例です。

C#
var user1 = new User { Id = 1, Name = "田中" };
var user2 = new User { Id = 1, Name = "佐藤" };

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

この例では、Nameが違ってもIdが同じなら同じユーザーとして扱っています。

11-6. ListをSort・OrderByで並び替えるサンプルコード

数値のリストをソートする例です。

C#
var numbers = new List<int> { 5, 2, 4, 1, 3 };

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5

独自クラスをOrderByで並び替える例です。

C#
class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
}
C#
var products = new List<Product>
{
new Product { Name = "Keyboard", Price = 3000 },
new Product { Name = "Mouse", Price = 1500 },
new Product { Name = "Monitor", Price = 20000 }
};

var sorted = products
.OrderBy(p => p.Price)
.ToList();

foreach (var product in sorted)
{
Console.WriteLine($"{product.Name}: {product.Price}");
}

出力例です。

C#
Mouse: 1500
Keyboard: 3000
Monitor: 20000

降順にしたい場合は、OrderByDescendingを使います。

C#
var sortedDesc = products
.OrderByDescending(p => p.Price)
.ToList();

12. C#の比較に関するよくある質問

C#の比較では、==EqualsCompareToの使い分けで迷うことがよくあります。

ここでは、よくある疑問を整理して回答します。

12-1. ==とEqualsはどちらを使うべき?

単純な数値比較なら==で問題ありません。

C#
if (a == b)
{
}

文字列比較でも、内容が等しいかを調べるだけなら==を使えます。

C#
if (name == "admin")
{
}

ただし、大文字小文字の扱いや比較ルールを明確にしたい場合は、string.Equalsを使うのがおすすめです。

C#
string.Equals(name, "admin", StringComparison.OrdinalIgnoreCase)

独自クラスでは、==が参照比較になることがあるため、値として比較したいならEqualsを適切に実装します。

12-2. stringの比較は==で問題ない?

文字列の内容が等しいかを単純に判定するなら、==で問題ありません。

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

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

ただし、次のような場合はstring.EqualsStringComparisonを指定する方が安全です。

C#
大文字小文字を無視したい
カルチャ依存を避けたい
比較ルールを明示したい
null安全に比較したい

例です。

C#
string.Equals(a, b, StringComparison.Ordinal)

大文字小文字を無視するなら、次のようにします。

C#
string.Equals(a, b, StringComparison.OrdinalIgnoreCase)

12-3. CompareToの戻り値は何を意味する?

CompareToの戻り値は、intです。

C#
int result = a.CompareTo(b);

意味は次のとおりです。

戻り値意味
0より小さい自分の方が小さい、または前に来る
0等しい
0より大きい自分の方が大きい、または後に来る

使い方は次のようになります。

C#
if (result < 0)
{
Console.WriteLine("前に来ます");
}
else if (result == 0)
{
Console.WriteLine("同じです");
}
else
{
Console.WriteLine("後に来ます");
}

具体的な戻り値が-11かではなく、符号で判断しましょう。

12-4. 参照が同じかどうかを比較するには?

参照が同じかどうかを明確に比較したい場合は、ReferenceEqualsを使います。

C#
object a = new object();
object b = a;

Console.WriteLine(object.ReferenceEquals(a, b)); // True

別々のインスタンスならfalseです。

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

Console.WriteLine(object.ReferenceEquals(x, y)); // False

==は型によってオーバーロードされていることがあるため、参照そのものを比較したい場合はReferenceEqualsを使うと意図が明確です。

12-5. null安全に比較するには?

文字列なら、string.Equalsを使うのが便利です。

C#
string? name = null;

bool result = string.Equals(
name,
"admin",
StringComparison.Ordinal
);

Console.WriteLine(result); // False

オブジェクト全般では、object.Equalsを使えます。

C#
object? a = null;
object? b = null;

Console.WriteLine(object.Equals(a, b)); // True

null判定なら、is nullまたはis not nullが読みやすいです。

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

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

12-6. JavaのequalsとC#のEqualsは同じ?

JavaのequalsとC#のEqualsは、どちらもオブジェクトの同値性を判定するためのメソッドという点では似ています。

ただし、言語仕様や慣習には違いがあります。

C#では、次のような比較方法を使い分けます。

C#
==演算子
Equalsメソッド
object.Equals
IEquatable<T>
record

C#のstringでは、==でも内容比較ができます。

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

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

独自クラスでは、Javaと同じようにEqualsをオーバーライドし、さらにGetHashCodeも実装する必要があります。

C#
public override bool Equals(object? obj)
{
return obj is User other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}

12-7. 比較演算子を独自クラスで使えるようにするには?

独自クラスで==!=を使いたい場合は、演算子をオーバーロードします。

C#
class User
{
public int Id { get; set; }

public override bool Equals(object? obj)
{
return obj is User other && Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}

public static bool operator ==(User? left, User? right)
{
return Equals(left, right);
}

public static bool operator !=(User? left, User? right)
{
return !Equals(left, right);
}
}

大小比較の演算子を使いたい場合は、<>もオーバーロードできます。

C#
public static bool operator <(User left, User right)
{
return left.Id < right.Id;
}

public static bool operator >(User left, User right)
{
return left.Id > right.Id;
}

ただし、比較演算子を増やすほど整合性を保つ必要があります。

独自クラスの並び順を定義したいだけなら、まずはIComparable<T>OrderByを使う方がシンプルです。

まとめ

C#の比較には、==EqualsCompareToCompareCompareOrdinalなど複数の方法があります。

それぞれの役割を整理すると、次のようになります。

方法主な用途
==等しいかを簡潔に判定する
!=等しくないかを判定する
<, >, <=, >=数値や日付などの大小関係を判定する
Equals値として等しいかを判定する
object.Equalsnull安全に等値比較する
string.Equals文字列の比較ルールを指定して比較する
CompareTo大小関係や並び順を判定する
string.Compare文字列の並び順を比較する
string.CompareOrdinalカルチャ非依存の序数比較をする

基本的な使い分けは、次のとおりです。

C#
// 数値の比較
a == b
a < b
a >= b

// 文字列が等しいか
string.Equals(a, b, StringComparison.Ordinal)

// 大文字小文字を無視した文字列比較
string.Equals(a, b, StringComparison.OrdinalIgnoreCase)

// 並び順の比較
a.CompareTo(b)
string.Compare(a, b, StringComparison.Ordinal)

// null安全な比較
object.Equals(a, b)

C#の比較で特に注意したいのは、参照型、文字列、null、浮動小数点数、独自クラスです。

参照型では==が参照比較になることがあり、文字列では比較ルールを明示した方が安全です。

独自クラスで値として比較したい場合は、EqualsGetHashCode、必要に応じてIEquatable<T>IComparable<T>を実装しましょう。

C#の比較を正しく理解すれば、条件分岐、検索、重複判定、ソート、DictionaryやHashSetの扱いまで、より安全で読みやすいコードを書けるようになります。