C#の引き算を初心者向けに解説|数値・日付・時間の差分計算とエラー対策

はじめに

C#でプログラムを書き始めると、足し算や掛け算と同じくらいよく使うのが「引き算」です。たとえば、商品価格から割引額を引いたり、所持金から購入金額を引いたり、開始日時と終了日時の差を求めたりする場面で、C#の引き算は頻繁に登場します。

C#の引き算は、基本的には「-」演算子を使うだけなので、とてもシンプルです。しかし、実際にコードを書いてみると、intやdouble、decimalなどの数値型の違い、小数計算の誤差、DateTimeを使った日付や時間の差分計算、Listや文字列での「引き算のような処理」など、初心者がつまずきやすいポイントもあります。

この記事では、C#の引き算について初心者向けにわかりやすく解説します。数値の引き算だけでなく、日付・時間の差分計算、配列やリストの差分取得、よくあるエラーと対策、実践的なコード例まで順番に紹介します。

1. C#の引き算の基本を初心者向けに解説

1-1. C#で引き算に使う演算子「-」とは

C#で引き算を行うときは、マイナス記号である「-」演算子を使います。

たとえば、10から3を引く場合は次のように書きます。

C#
int result = 10 - 3;
Console.WriteLine(result);

実行結果は次のようになります。

C#
7

このように、C#では数学と同じような感覚で引き算を書くことができます。

「-」演算子は、数値同士の引き算だけでなく、日付同士の差分計算や、負の数を表すときにも使われます。

C#
int number = -5;

この場合の「-」は、5を引くという意味ではなく、「マイナス5」という負の数を表しています。C#の引き算を理解するときは、「計算としてのマイナス」と「負の数を表すマイナス」の両方を意識しておくとよいでしょう。

1-2. int・double・decimalで引き算する基本コード

C#では、扱う数値の種類によって型を使い分けます。代表的な数値型には、int、double、decimalがあります。

intは整数を扱う型です。

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

int result = a - b;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
12

doubleは小数を扱う型です。

C#
double a = 10.5;
double b = 3.2;

double result = a - b;

Console.WriteLine(result);

実行結果は、環境や値によって細かい表示が変わることがありますが、基本的には次のような結果になります。

C#
7.3

decimalも小数を扱う型ですが、金額計算など、より正確な小数計算に向いています。decimal型の数値リテラルには、末尾に m または M を付けます。

C#
decimal price = 1000.00m;
decimal discount = 250.50m;

decimal result = price - discount;

Console.WriteLine(result);

実行結果は次のようになります。

C#
749.50

C#の引き算では、どの数値型を使うかによって、計算結果の扱い方が変わります。整数だけならint、小数を含む一般的な計算ならdouble、金額計算ならdecimalを使うのが基本です。

1-3. 変数同士・変数と数値リテラルを引き算する方法

C#では、変数同士を引き算することも、変数と直接書いた数値を引き算することもできます。

変数同士を引き算する例です。

C#
int total = 100;
int used = 30;

int remaining = total - used;

Console.WriteLine(remaining);

実行結果は次のとおりです。

C#
70

次に、変数と数値リテラルを引き算する例です。数値リテラルとは、コードに直接書いた数値のことです。

C#
int score = 80;

int result = score - 10;

Console.WriteLine(result);

実行結果は次のようになります。

C#
70

反対に、数値リテラルから変数を引くこともできます。

C#
int used = 25;

int result = 100 - used;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
75

C#の引き算では、左側の値から右側の値を引くという順番が重要です。a - bb - a では結果が変わるため、どちらからどちらを引くのかを意識しましょう。

1-4. 引き算の結果を別の変数に代入する書き方

引き算の結果は、別の変数に代入して使うことができます。

C#
int price = 500;
int coupon = 100;

int payment = price - coupon;

Console.WriteLine(payment);

このコードでは、price - coupon の結果を payment という変数に代入しています。

実行結果は次のとおりです。

C#
400

計算結果を変数に入れておくと、その後の処理で再利用できます。

C#
int price = 500;
int coupon = 100;

int payment = price - coupon;
int taxIncluded = payment + 40;

Console.WriteLine(taxIncluded);

このように、C#では引き算の結果を変数に保存し、さらに別の計算に使うことができます。

また、変数名は意味がわかる名前にすると、コードが読みやすくなります。

C#
int totalAmount = 10000;
int paidAmount = 3000;

int unpaidAmount = totalAmount - paidAmount;

Console.WriteLine(unpaidAmount);

unpaidAmount は「未払い金額」という意味なので、コードを見たときに何を計算しているのかがわかりやすくなります。

1-5. Console.WriteLineで計算結果を確認する方法

C#の学習では、Console.WriteLine を使って計算結果を画面に表示しながら確認するのが基本です。

C#
int a = 15;
int b = 4;

int result = a - b;

Console.WriteLine(result);

実行結果は次のようになります。

C#
11

文字列と組み合わせて表示することもできます。

C#
int a = 15;
int b = 4;

int result = a - b;

Console.WriteLine("計算結果は " + result + " です。");

実行結果は次のとおりです。

C#
計算結果は 11 です。

文字列補間を使うと、より読みやすく書けます。

C#
int a = 15;
int b = 4;

int result = a - b;

Console.WriteLine($"計算結果は {result} です。");

文字列補間では、文字列の前に $ を付け、変数や式を {} の中に書きます。

C#
Console.WriteLine($"{a} - {b} = {result}");

実行結果は次のようになります。

C#
15 - 4 = 11

初心者のうちは、C#の引き算を書いたら Console.WriteLine でこまめに結果を確認すると、計算の流れを理解しやすくなります。

2. 数値型ごとの引き算の違い

2-1. int型で整数を引き算する

int型は、整数を扱うための基本的な型です。小数を含まない数値の引き算に使います。

C#
int stock = 50;
int sold = 12;

int remaining = stock - sold;

Console.WriteLine(remaining);

実行結果は次のとおりです。

C#
38

int型では、小数点以下の値を扱うことはできません。

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

int result = a - b;

Console.WriteLine(result);

この場合は、単純に整数同士の引き算なので問題ありません。

ただし、int型に小数を代入しようとするとエラーになります。

C#
int number = 10.5;

このコードはコンパイルエラーになります。int型は整数専用なので、10.5のような小数は代入できません。

整数の個数、人数、点数、在庫数、回数などを扱う場合は、int型で引き算するのがわかりやすいです。

2-2. double型で小数を引き算する

double型は、小数を扱うための数値型です。距離、割合、平均値、測定値など、小数を含む計算に使われます。

C#
double height1 = 175.5;
double height2 = 168.2;

double difference = height1 - height2;

Console.WriteLine(difference);

実行結果は次のようになります。

C#
7.3

double型は便利ですが、内部的には2進数で小数を表すため、計算結果にわずかな誤差が出ることがあります。

C#
double a = 0.3;
double b = 0.1;

double result = a - b;

Console.WriteLine(result);

期待する結果は 0.2 ですが、実際には次のように表示される場合があります。

C#
0.19999999999999998

これはC#のバグではなく、double型の仕組みによるものです。科学計算や一般的な小数計算ではdoubleがよく使われますが、金額のように誤差を避けたい計算ではdecimal型を使うことを検討しましょう。

2-3. decimal型で金額や厳密な小数を引き算する

decimal型は、金額計算など、誤差をできるだけ避けたい場面で使われます。

C#
decimal price = 1200.00m;
decimal discount = 300.50m;

decimal payment = price - discount;

Console.WriteLine(payment);

実行結果は次のとおりです。

C#
899.50

decimal型の数値には、末尾に m を付ける必要があります。

C#
decimal amount = 100.0m;

m を付けないと、C#では小数リテラルをdouble型として扱います。そのため、次のようなコードはエラーになります。

C#
decimal amount = 100.0;

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

C#
decimal amount = 100.0m;

金額、請求額、割引額、税額、残高などを扱う場合は、doubleではなくdecimalを使うのが一般的です。

C#
decimal balance = 10000m;
decimal withdrawal = 2750m;

decimal remainingBalance = balance - withdrawal;

Console.WriteLine($"残高は {remainingBalance} 円です。");

実行結果は次のようになります。

C#
残高は 7250 円です。

C#の引き算で金額を扱う場合は、decimal型を使うことで小数誤差によるトラブルを避けやすくなります。

2-4. float型を使う場合の注意点

float型も小数を扱う型ですが、doubleよりも精度が低い型です。float型の数値リテラルには、末尾に f または F を付けます。

C#
float a = 10.5f;
float b = 3.2f;

float result = a - b;

Console.WriteLine(result);

float型は、メモリ使用量を抑えたい場合や、ゲーム開発、グラフィックス処理などで使われることがあります。

ただし、通常のC#アプリケーションで小数を扱う場合は、doubleを使うことが多いです。金額計算にはdecimalを使うのが基本です。

floatでも小数誤差が発生することがあります。

C#
float a = 0.3f;
float b = 0.1f;

float result = a - b;

Console.WriteLine(result);

小数を正確に扱いたい場合には、floatは向いていません。float、double、decimalの違いを簡単にまとめると、次のようになります。

主な用途
int整数の計算
float精度より軽さを重視する小数計算
double一般的な小数計算
decimal金額など正確さが重要な小数計算

C#の引き算では、目的に合った型を選ぶことが大切です。

2-5. 型が異なる数値を引き算するときの暗黙的変換と明示的キャスト

C#では、異なる型の数値を引き算すると、型変換が発生することがあります。

たとえば、int型とdouble型を引き算すると、int型の値がdouble型として扱われ、結果もdouble型になります。

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

double result = a - b;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
6.5

この場合、a はint型ですが、計算時にはdouble型に変換されます。これを暗黙的変換といいます。

一方、double型の計算結果をint型に入れたい場合は、明示的キャストが必要です。

C#
double a = 10.8;
double b = 3.2;

int result = (int)(a - b);

Console.WriteLine(result);

実行結果は次のようになります。

C#
7

a - b の結果は 7.6 ですが、int型にキャストすると小数点以下が切り捨てられます。

decimal型とdouble型をそのまま引き算することはできません。

C#
decimal a = 10.0m;
double b = 3.0;

decimal result = a - b;

このコードはエラーになります。decimalとdoubleは自動で混ぜて計算できないため、型をそろえる必要があります。

C#
decimal a = 10.0m;
decimal b = 3.0m;

decimal result = a - b;

Console.WriteLine(result);

C#の引き算でエラーを避けるには、計算する値の型をそろえることが重要です。

3. C#でよく使う引き算の書き方

3-1. a = a - bの基本形

C#で変数の値から別の値を引き、その結果を同じ変数に戻す場合、次のように書きます。

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

a = a - b;

Console.WriteLine(a);

実行結果は次のとおりです。

C#
7

このコードでは、もともと a には10が入っています。a - b によって10から3を引き、その結果である7を再び a に代入しています。

在庫数を減らす処理にも使えます。

C#
int stock = 100;
int orderCount = 25;

stock = stock - orderCount;

Console.WriteLine($"残り在庫: {stock}");

実行結果は次のようになります。

C#
残り在庫: 75

この書き方は、C#の引き算の基本形として必ず覚えておきたい書き方です。

3-2. 複合代入演算子「-=」を使う方法

a = a - b は、複合代入演算子 -= を使って短く書くことができます。

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

a -= b;

Console.WriteLine(a);

実行結果は次のとおりです。

C#
7

a -= b は、次のコードと同じ意味です。

C#
a = a - b;

在庫数を減らす処理も、-= を使うと簡潔に書けます。

C#
int stock = 100;
int orderCount = 25;

stock -= orderCount;

Console.WriteLine(stock);

実行結果は次のようになります。

C#
75

-= は、現在の値から指定した値を引いて更新したいときによく使います。

C#
int hp = 100;
int damage = 30;

hp -= damage;

Console.WriteLine($"残りHP: {hp}");

ゲームのHP計算、残高計算、在庫管理などでよく登場する書き方です。

3-3. デクリメント演算子「--」で1だけ引く方法

C#で変数の値を1だけ減らしたい場合は、デクリメント演算子 -- を使います。

C#
int count = 10;

count--;

Console.WriteLine(count);

実行結果は次のとおりです。

C#
9

count-- は、次のコードと同じ意味です。

C#
count = count - 1;

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

C#
count -= 1;

ループ処理やカウントダウン処理では、デクリメント演算子がよく使われます。

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

実行結果は次のようになります。

C#
5
4
3
2
1

-- には、後置形式と前置形式があります。

C#
count--;
--count;

単独で使う場合は、どちらも変数を1減らします。ただし、式の中で使う場合は評価のタイミングが異なります。

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

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

この場合、b には減る前の5が入り、その後で a が4になります。

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

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

この場合、先に a が4になり、その4が b に入ります。初心者のうちは、混乱を避けるために -- は単独の行で使うとわかりやすいです。

3-4. 負の数を使った引き算の考え方

C#では、負の数を使った引き算もできます。

C#
int result = 10 - (-3);

Console.WriteLine(result);

実行結果は次のとおりです。

C#
13

数学と同じように、負の数を引くと足し算と同じ結果になります。

C#
10 - (-3)

これは次の計算と同じです。

C#
10 + 3

変数を使った場合も同じです。

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

int result = a - b;

Console.WriteLine(result);

実行結果は次のようになります。

C#
13

また、引き算の結果が負の数になることもあります。

C#
int money = 500;
int price = 800;

int balance = money - price;

Console.WriteLine(balance);

実行結果は次のとおりです。

C#
-300

このように、C#の引き算では結果がマイナスになることも自然に扱えます。ただし、所持金や在庫数など、マイナスになってはいけない値を扱う場合は、事前にチェックする処理が必要です。

C#
int money = 500;
int price = 800;

if (money >= price)
{
money -= price;
Console.WriteLine($"残金: {money}");
}
else
{
Console.WriteLine("お金が足りません。");
}

3-5. 計算式の中で引き算を組み合わせる方法

C#では、引き算を足し算、掛け算、割り算などと組み合わせて使うことができます。

C#
int result = 100 - 20 + 5;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
85

掛け算や割り算と組み合わせる場合、演算子の優先順位に注意が必要です。

C#
int result = 100 - 20 * 3;

Console.WriteLine(result);

この場合、先に 20 * 3 が計算され、その結果を100から引きます。

C#
40

先に引き算を行いたい場合は、かっこを使います。

C#
int result = (100 - 20) * 3;

Console.WriteLine(result);

実行結果は次のようになります。

C#
240

C#では、かっこを使うことで計算順序を明確にできます。複雑な計算式では、読みやすさのためにも積極的にかっこを使いましょう。

C#
decimal price = 1000m;
decimal discount = 200m;
decimal taxRate = 0.1m;

decimal total = (price - discount) + ((price - discount) * taxRate);

Console.WriteLine(total);

同じ計算を何度も書くと読みにくくなるため、途中結果を変数に入れるとよりわかりやすくなります。

C#
decimal price = 1000m;
decimal discount = 200m;
decimal taxRate = 0.1m;

decimal discountedPrice = price - discount;
decimal tax = discountedPrice * taxRate;
decimal total = discountedPrice + tax;

Console.WriteLine(total);

4. 日付・時間の引き算で差分を計算する方法

4-1. DateTime同士を引き算して日数差を求める

C#では、DateTime同士を引き算すると、日付や時刻の差分を求めることができます。DateTime同士の引き算の結果は、TimeSpan型になります。

C#
DateTime startDate = new DateTime(2025, 1, 1);
DateTime endDate = new DateTime(2025, 1, 10);

TimeSpan difference = endDate - startDate;

Console.WriteLine(difference.Days);

実行結果は次のとおりです。

C#
9

2025年1月1日から2025年1月10日までは、9日間の差があります。

日付の差分を求めるときは、どちらからどちらを引くかに注意しましょう。

C#
TimeSpan difference = startDate - endDate;

Console.WriteLine(difference.Days);

この場合、結果はマイナスになります。

C#
-9

日数差を常に正の値で扱いたい場合は、Math.Abs を使う方法があります。

C#
int days = Math.Abs((endDate - startDate).Days);

Console.WriteLine(days);

ただし、開始日と終了日の意味がある場合は、単純に絶対値にするのではなく、日付の順番が正しいかを確認する処理を入れると安全です。

4-2. TimeSpanで時間・分・秒の差分を取得する

DateTime同士の引き算で得られるTimeSpanには、日数、時間、分、秒などの差分情報が含まれています。

C#
DateTime start = new DateTime(2025, 1, 1, 9, 30, 0);
DateTime end = new DateTime(2025, 1, 1, 12, 45, 30);

TimeSpan diff = end - start;

Console.WriteLine(diff.Hours);
Console.WriteLine(diff.Minutes);
Console.WriteLine(diff.Seconds);

実行結果は次のようになります。

C#
3
15
30

この例では、差分は3時間15分30秒です。

合計時間を小数で取得したい場合は、TotalHours を使います。

C#
Console.WriteLine(diff.TotalHours);

合計分を取得したい場合は、TotalMinutes を使います。

C#
Console.WriteLine(diff.TotalMinutes);

HoursTotalHours は意味が異なります。

C#
TimeSpan span = new TimeSpan(1, 2, 30, 0);

Console.WriteLine(span.Days);
Console.WriteLine(span.Hours);
Console.WriteLine(span.TotalHours);

この場合、TimeSpanは1日と2時間30分を表します。Hours は「日数を除いた時間部分」の2を返します。一方、TotalHours は全体を時間に換算した26.5を返します。

日付・時間の引き算では、部分の値がほしいのか、合計値がほしいのかを意識することが大切です。

4-3. DateTimeから日数や時間を引く方法

DateTimeから特定の日数や時間を引きたい場合は、AddDaysAddHours に負の値を指定します。

C#
DateTime today = new DateTime(2025, 1, 10);

DateTime yesterday = today.AddDays(-1);

Console.WriteLine(yesterday);

このコードでは、2025年1月10日から1日引いて、2025年1月9日を求めています。

7日前の日付を求める場合は、次のように書きます。

C#
DateTime today = DateTime.Today;

DateTime sevenDaysAgo = today.AddDays(-7);

Console.WriteLine(sevenDaysAgo);

時間を引く場合は、AddHours を使います。

C#
DateTime now = DateTime.Now;

DateTime twoHoursAgo = now.AddHours(-2);

Console.WriteLine(twoHoursAgo);

分を引く場合は、AddMinutes を使います。

C#
DateTime now = DateTime.Now;

DateTime thirtyMinutesAgo = now.AddMinutes(-30);

Console.WriteLine(thirtyMinutesAgo);

TimeSpanを使ってDateTimeから時間を引くこともできます。

C#
DateTime now = DateTime.Now;
TimeSpan span = new TimeSpan(2, 0, 0);

DateTime result = now - span;

Console.WriteLine(result);

この場合、現在時刻から2時間前の日時を求められます。

4-4. DateOnly・TimeOnlyで日付や時刻の差を扱う場合

C#では、DateOnlyやTimeOnlyを使って日付だけ、時刻だけを扱うこともできます。

DateOnlyは、年月日のみを表す型です。時刻を含まない日付を扱いたい場合に便利です。

C#
DateOnly start = new DateOnly(2025, 1, 1);
DateOnly end = new DateOnly(2025, 1, 10);

int days = end.DayNumber - start.DayNumber;

Console.WriteLine(days);

実行結果は次のとおりです。

C#
9

DateOnly同士を単純に end - start のように引き算することはできないため、日数差を求める場合は DayNumber を使う方法があります。

TimeOnlyは、時刻だけを表す型です。TimeOnly同士の差を求める場合は、ToTimeSpan を使ってTimeSpanに変換するとわかりやすいです。

C#
TimeOnly start = new TimeOnly(9, 30);
TimeOnly end = new TimeOnly(12, 45);

TimeSpan diff = end.ToTimeSpan() - start.ToTimeSpan();

Console.WriteLine(diff);

実行結果は次のようになります。

C#
03:15:00

ただし、TimeOnlyで日付をまたぐ時間差を扱う場合は注意が必要です。

C#
TimeOnly start = new TimeOnly(23, 0);
TimeOnly end = new TimeOnly(1, 0);

TimeSpan diff = end.ToTimeSpan() - start.ToTimeSpan();

Console.WriteLine(diff);

この場合、結果は単純な引き算になるため、マイナスの時間になります。夜23時から翌日1時までの2時間を求めたい場合は、日付を含むDateTimeで扱うか、日付またぎを考慮した処理を追加しましょう。

4-5. 日付計算でタイムゾーンや日付の境界に注意するポイント

DateTimeの引き算では、タイムゾーンや日付の境界に注意が必要です。

たとえば、ローカル時刻とUTC時刻を混ぜて計算すると、想定と異なる差分になることがあります。

C#
DateTime localTime = DateTime.Now;
DateTime utcTime = DateTime.UtcNow;

TimeSpan diff = localTime - utcTime;

Console.WriteLine(diff);

このようなコードでは、ローカル時刻とUTC時刻の差が出るため、意図しない結果に見えることがあります。

タイムゾーンを意識して扱う場合は、DateTimeOffsetを使うと便利です。

C#
DateTimeOffset start = DateTimeOffset.Now;
DateTimeOffset end = start.AddHours(3);

TimeSpan diff = end - start;

Console.WriteLine(diff.TotalHours);

また、日付だけの差を求めたい場合は、時刻部分を含めないように注意します。

C#
DateTime start = new DateTime(2025, 1, 1, 23, 0, 0);
DateTime end = new DateTime(2025, 1, 2, 1, 0, 0);

TimeSpan diff = end - start;

Console.WriteLine(diff.TotalDays);

この場合、日付は1月1日から1月2日に変わっていますが、実際の差は2時間です。そのため、TotalDays は1ではなく、約0.0833になります。

日付の差を「カレンダー上の日数」として扱いたい場合は、Dateプロパティを使って時刻を切り捨てる方法があります。

C#
DateTime start = new DateTime(2025, 1, 1, 23, 0, 0);
DateTime end = new DateTime(2025, 1, 2, 1, 0, 0);

int days = (end.Date - start.Date).Days;

Console.WriteLine(days);

実行結果は次のとおりです。

C#
1

C#の日付・時間の引き算では、実際の経過時間を求めたいのか、カレンダー上の日付差を求めたいのかを明確にすることが大切です。

5. 配列・リスト・文字列で「引き算のような処理」を行う方法

5-1. Listから要素を削除する処理は数値の引き算とは異なる

C#では、数値の引き算には「-」演算子を使います。しかし、Listから要素を削除する処理は、数値の引き算とは異なります。

たとえば、Listから特定の要素を取り除きたい場合は、Remove メソッドを使います。

C#
List<string> fruits = new List<string> { "Apple", "Banana", "Orange" };

fruits.Remove("Banana");

foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}

実行結果は次のとおりです。

C#
Apple
Orange

Listに対して、次のように - 演算子で要素を引くことはできません。

C#
fruits = fruits - "Banana";

このような書き方はコンパイルエラーになります。

複数の要素を条件で削除したい場合は、RemoveAll を使えます。

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

numbers.RemoveAll(n => n % 2 == 0);

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

実行結果は次のようになります。

C#
1
3
5

Listの削除は「引き算のように見える処理」ですが、実際にはコレクション操作です。数値計算とは書き方が違う点を覚えておきましょう。

5-2. LINQのExceptで差集合を求める方法

2つのリストを比較して、一方にだけ存在する要素を取り出したい場合は、LINQの Except を使います。これは集合の差分、つまり差集合を求める処理です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

List<int> listA = new List<int> { 1, 2, 3, 4, 5 };
List<int> listB = new List<int> { 2, 4 };

IEnumerable<int> result = listA.Except(listB);

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

実行結果は次のとおりです。

C#
1
3
5

listA.Except(listB) は、listAからlistBに含まれる要素を除いた結果を返します。

文字列のリストでも使えます。

C#
List<string> oldMembers = new List<string> { "Tanaka", "Suzuki", "Sato" };
List<string> newMembers = new List<string> { "Suzuki", "Sato" };

IEnumerable<string> removedMembers = oldMembers.Except(newMembers);

foreach (string member in removedMembers)
{
Console.WriteLine(member);
}

実行結果は次のようになります。

C#
Tanaka

Exceptは、リストや配列の差分を求めたいときに便利です。ただし、重複要素の扱いには注意が必要です。Exceptは集合演算として動作するため、重複をそのまま数えて差し引く処理とは異なります。

5-3. 文字列から一部を取り除く場合のReplace・Removeの使い方

文字列から一部を取り除きたい場合も、「-」演算子は使いません。代わりに、ReplaceRemove を使います。

特定の文字列を取り除く場合は、Replace を使って空文字に置き換えます。

C#
string text = "C# Programming";

string result = text.Replace(" Programming", "");

Console.WriteLine(result);

実行結果は次のとおりです。

C#
C#

特定の位置から文字を削除する場合は、Remove を使います。

C#
string text = "Hello World";

string result = text.Remove(5);

Console.WriteLine(result);

実行結果は次のようになります。

C#
Hello

開始位置と削除する文字数を指定することもできます。

C#
string text = "Hello World";

string result = text.Remove(5, 1);

Console.WriteLine(result);

実行結果は次のとおりです。

C#
HelloWorld

この例では、5番目の位置にある半角スペースを1文字削除しています。

文字列から不要な文字を取り除きたい場合は、数値の引き算ではなく、文字列操作のメソッドを使うと覚えておきましょう。

5-4. コレクションの件数差を計算する方法

リストや配列の要素数の差を求めたい場合は、CountやLengthを使って数値として引き算します。

Listの件数差を求める例です。

C#
List<string> before = new List<string> { "A", "B", "C", "D" };
List<string> after = new List<string> { "A", "B" };

int difference = before.Count - after.Count;

Console.WriteLine(difference);

実行結果は次のとおりです。

C#
2

配列の場合は、Lengthを使います。

C#
string[] oldItems = { "A", "B", "C" };
string[] newItems = { "A", "B", "C", "D", "E" };

int difference = newItems.Length - oldItems.Length;

Console.WriteLine(difference);

実行結果は次のようになります。

C#
2

件数差は単純な数値の引き算です。一方で、どの要素が増えたのか、どの要素が減ったのかを知りたい場合は、Exceptなどを使って差分を取得する必要があります。

C#
List<string> oldItems = new List<string> { "A", "B", "C" };
List<string> newItems = new List<string> { "A", "B", "C", "D", "E" };

var addedItems = newItems.Except(oldItems);

foreach (string item in addedItems)
{
Console.WriteLine(item);
}

実行結果は次のとおりです。

C#
D
E

「件数の差」と「中身の差」は別のものとして考えましょう。

5-5. 引き算と削除処理を混同しないための考え方

C#では、引き算と削除処理を混同しないことが重要です。

数値の差を求める場合は、- 演算子を使います。

C#
int result = 10 - 3;

変数の値を減らす場合も、--= を使います。

C#
int stock = 100;
stock -= 20;

一方で、Listから要素を削除する場合は、RemoveやRemoveAllを使います。

C#
list.Remove(item);

2つのコレクションの差分を求める場合は、Exceptを使います。

C#
var diff = listA.Except(listB);

文字列から一部を取り除く場合は、ReplaceやRemoveを使います。

C#
string result = text.Replace("不要", "");

このように、C#では「何を引きたいのか」によって使う書き方が変わります。

数値を引くなら -、値を更新するなら -= 、1だけ減らすなら --、Listの要素を削除するなら Remove、差集合を求めるなら Except と整理して覚えると、混乱しにくくなります。

6. C#の引き算で発生しやすいエラーと対策

6-1. 型が合わないときに起きるコンパイルエラー

C#の引き算では、型が合わないとコンパイルエラーになることがあります。

たとえば、文字列と数値をそのまま引き算することはできません。

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

int result = a - b;

このコードはエラーになります。a は文字列であり、数値ではないためです。

文字列として入力された数値を引き算したい場合は、intに変換する必要があります。

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

int number = int.Parse(a);
int result = number - b;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
7

ただし、int.Parse は変換できない文字列が渡されると例外が発生します。安全に変換したい場合は、int.TryParse を使います。

C#
string input = "10";

if (int.TryParse(input, out int number))
{
int result = number - 3;
Console.WriteLine(result);
}
else
{
Console.WriteLine("数値に変換できません。");
}

型が異なる値を引き算するときは、まず数値として計算できる型かどうかを確認しましょう。

6-2. nullを含む値を引き算するときの注意点

C#でnullを含む値を扱う場合は注意が必要です。通常のint型にはnullを代入できませんが、nullable型である int? にはnullを代入できます。

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

int? result = a - b;

Console.WriteLine(result);

この場合、結果はnullになります。nullable型同士の計算では、どちらかがnullだと結果もnullになります。

nullの場合に0として扱いたい場合は、null合体演算子 ?? を使います。

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

int result = (a ?? 0) - (b ?? 0);

Console.WriteLine(result);

実行結果は次のとおりです。

C#
10

nullable型の値を通常のintに変換したい場合は、Value プロパティを使うこともできますが、nullのときに例外が発生します。

C#
int? a = null;

int value = a.Value;

安全に扱うには、HasValue を確認します。

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

if (a.HasValue && b.HasValue)
{
int result = a.Value - b.Value;
Console.WriteLine(result);
}
else
{
Console.WriteLine("値がありません。");
}

C#の引き算でnullを扱う場合は、「nullならどうするか」を事前に決めておくことが大切です。

6-3. オーバーフローが発生するケースとcheckedの使い方

int型には扱える範囲があります。範囲を超える計算をすると、オーバーフローが発生することがあります。

C#
int min = int.MinValue;

int result = min - 1;

Console.WriteLine(result);

通常の実行では、オーバーフローして予想外の値になることがあります。オーバーフローを検出したい場合は、checked を使います。

C#
checked
{
int min = int.MinValue;
int result = min - 1;

Console.WriteLine(result);
}

この場合、範囲を超えた計算が行われると例外が発生します。

特定の式だけをcheckedにすることもできます。

C#
int min = int.MinValue;

int result = checked(min - 1);

反対に、オーバーフローのチェックをしないことを明示する unchecked もあります。

C#
unchecked
{
int min = int.MinValue;
int result = min - 1;

Console.WriteLine(result);
}

金額や在庫数など、範囲を超えると困る計算では、checkedを使って異常を検出することを検討しましょう。

また、intより大きな範囲を扱いたい場合は、long型を使う方法もあります。

C#
long a = 3000000000L;
long b = 1000000000L;

long result = a - b;

Console.WriteLine(result);

扱う数値の大きさに応じて、適切な型を選ぶことが重要です。

6-4. 小数計算で誤差が出る理由とdecimalの使い分け

doubleやfloatで小数を引き算すると、誤差が出ることがあります。

C#
double a = 0.3;
double b = 0.1;

double result = a - b;

Console.WriteLine(result);

期待する結果は0.2ですが、実際には次のような結果になることがあります。

C#
0.19999999999999998

これは、doubleやfloatが小数を2進数で近似して扱うためです。多くの小数はコンピューター内部で正確に表現できないため、わずかな誤差が発生します。

金額計算では、このような誤差が問題になることがあります。

C#
double price = 0.3;
double discount = 0.1;

double result = price - discount;

Console.WriteLine(result);

金額を扱うなら、decimalを使うほうが適しています。

C#
decimal price = 0.3m;
decimal discount = 0.1m;

decimal result = price - discount;

Console.WriteLine(result);

実行結果は次のようになります。

C#
0.2

ただし、decimalはdoubleよりも計算速度が遅くなる場合があります。そのため、用途に応じて使い分けましょう。

一般的な目安は次のとおりです。

用途おすすめの型
整数計算int、long
一般的な小数計算double
金額や正確な小数計算decimal
軽量な小数計算float

C#の引き算で小数を扱う場合は、誤差が許される計算かどうかを考えて型を選ぶことが大切です。

6-5. DateTimeの引き算で想定外の結果になる原因

DateTimeの引き算で想定外の結果になる原因には、時刻部分、タイムゾーン、日付の順番などがあります。

たとえば、日付だけを比較しているつもりでも、DateTimeには時刻が含まれています。

C#
DateTime start = new DateTime(2025, 1, 1, 23, 0, 0);
DateTime end = new DateTime(2025, 1, 2, 1, 0, 0);

TimeSpan diff = end - start;

Console.WriteLine(diff.TotalDays);

日付は1日違いますが、実際の差は2時間です。そのため、1日差ではなく、約0.0833日として計算されます。

カレンダー上の日数差を求めたい場合は、Dateプロパティを使います。

C#
int days = (end.Date - start.Date).Days;

Console.WriteLine(days);

また、終了日時から開始日時を引くつもりが、逆に書いてしまうとマイナスになります。

C#
TimeSpan diff = start - end;

日付計算では、次の点を確認しましょう。

確認ポイント内容
引く順番終了日時 - 開始日時になっているか
時刻部分日付だけでよいのか、時刻も含めるのか
タイムゾーンローカル時刻とUTCを混ぜていないか
結果の取得方法DaysとTotalDaysの違いを理解しているか

DateTimeの引き算は便利ですが、時刻やタイムゾーンを含むため、単純な日付差とは異なる場合があります。

7. 実践例で学ぶC#の引き算

7-1. 商品価格から割引額を引く計算

商品価格から割引額を引いて、支払金額を求める例です。金額計算なのでdecimal型を使います。

C#
decimal price = 5000m;
decimal discount = 1200m;

decimal payment = price - discount;

Console.WriteLine($"支払金額は {payment} 円です。");

実行結果は次のとおりです。

C#
支払金額は 3800 円です。

割引後の金額がマイナスにならないようにチェックすることも大切です。

C#
decimal price = 1000m;
decimal discount = 1500m;

decimal payment = price - discount;

if (payment < 0)
{
payment = 0;
}

Console.WriteLine($"支払金額は {payment} 円です。");

このように、実務では単純に引き算するだけでなく、計算結果が妥当かどうかも確認します。

7-2. 所持金から購入金額を引く計算

所持金から購入金額を引く例です。

C#
int money = 3000;
int price = 1200;

money -= price;

Console.WriteLine($"残金は {money} 円です。");

実行結果は次のとおりです。

C#
残金は 1800 円です。

購入前に所持金が足りるかを確認する処理を入れると、より安全です。

C#
int money = 1000;
int price = 1200;

if (money >= price)
{
money -= price;
Console.WriteLine($"購入しました。残金は {money} 円です。");
}
else
{
Console.WriteLine("所持金が足りません。");
}

実行結果は次のようになります。

C#
所持金が足りません。

所持金、残高、ポイントなどを扱う場合は、引き算の結果がマイナスになってよいかどうかを考えることが重要です。

7-3. 開始日時と終了日時から経過時間を求める計算

開始日時と終了日時から経過時間を求めるには、DateTime同士を引き算します。

C#
DateTime start = new DateTime(2025, 1, 1, 9, 0, 0);
DateTime end = new DateTime(2025, 1, 1, 17, 30, 0);

TimeSpan elapsed = end - start;

Console.WriteLine($"経過時間は {elapsed.TotalHours} 時間です。");

実行結果は次のとおりです。

C#
経過時間は 8.5 時間です。

時間、分、秒に分けて表示することもできます。

C#
Console.WriteLine($"{elapsed.Hours}時間{elapsed.Minutes}分");

実行結果は次のようになります。

C#
8時間30分

勤務時間、処理時間、滞在時間、学習時間などを計算する場合に使える基本的な書き方です。

7-4. 残り日数を表示するカウントダウン処理

指定した日付までの残り日数を表示する例です。

C#
DateTime today = DateTime.Today;
DateTime targetDate = new DateTime(2025, 12, 31);

TimeSpan remaining = targetDate - today;

Console.WriteLine($"残り {remaining.Days} 日です。");

今日の日付から目標日までの日数を計算できます。

目標日を過ぎている場合の処理も入れておくと親切です。

C#
DateTime today = DateTime.Today;
DateTime targetDate = new DateTime(2025, 12, 31);

TimeSpan remaining = targetDate - today;

if (remaining.Days > 0)
{
Console.WriteLine($"残り {remaining.Days} 日です。");
}
else if (remaining.Days == 0)
{
Console.WriteLine("今日が目標日です。");
}
else
{
Console.WriteLine("目標日は過ぎています。");
}

カウントダウン処理では、現在日付と目標日付の差をTimeSpanで求めるのが基本です。

7-5. 配列やリストの差分を取得する処理

2つのリストの差分を取得する例です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

List<string> oldList = new List<string> { "A", "B", "C" };
List<string> newList = new List<string> { "B", "C", "D" };

var removedItems = oldList.Except(newList);
var addedItems = newList.Except(oldList);

Console.WriteLine("削除された要素:");
foreach (string item in removedItems)
{
Console.WriteLine(item);
}

Console.WriteLine("追加された要素:");
foreach (string item in addedItems)
{
Console.WriteLine(item);
}

実行結果は次のとおりです。

C#
削除された要素:
A
追加された要素:
D

このように、リストの中身の差分を求めるにはExceptを使います。

件数だけを比較したい場合は、Countを引き算します。

C#
int countDifference = newList.Count - oldList.Count;

Console.WriteLine(countDifference);

この例では、どちらも3件なので結果は0です。件数は同じでも、中身が違うことがあります。そのため、件数差と要素差は分けて考えることが大切です。

8. C#の引き算を正しく使うためのポイント

8-1. 用途に合った数値型を選ぶ

C#の引き算を正しく使うには、まず用途に合った数値型を選ぶことが重要です。

整数だけを扱うならintを使います。

C#
int count = 10;
int used = 3;

int remaining = count - used;

大きな整数を扱うならlongを使います。

C#
long total = 5000000000L;
long used = 1200000000L;

long remaining = total - used;

一般的な小数ならdoubleを使います。

C#
double distance = 10.5;
double moved = 3.2;

double remaining = distance - moved;

金額計算ならdecimalを使います。

C#
decimal price = 1000m;
decimal discount = 250m;

decimal payment = price - discount;

型選びを間違えると、コンパイルエラーや小数誤差、オーバーフローの原因になります。

8-2. 計算結果の型を意識する

C#では、引き算の結果がどの型になるかを意識する必要があります。

int同士の引き算は、基本的にintになります。

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

int result = a - b;

intとdoubleを引き算すると、結果はdoubleになります。

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

double result = a - b;

decimalを使う場合は、計算相手もdecimalにそろえます。

C#
decimal a = 10.0m;
decimal b = 3.0m;

decimal result = a - b;

計算結果を別の型に代入するときは、必要に応じてキャストします。

C#
double a = 10.8;
double b = 3.2;

int result = (int)(a - b);

ただし、キャストすると小数点以下が切り捨てられるなど、値が変わることがあります。計算結果の型と値の変化に注意しましょう。

8-3. 小数や金額計算では誤差に注意する

doubleやfloatは便利ですが、小数計算で誤差が出ることがあります。

C#
double result = 0.3 - 0.1;

Console.WriteLine(result);

結果がぴったり0.2にならない場合があります。

金額計算ではdecimalを使うのが基本です。

C#
decimal result = 0.3m - 0.1m;

Console.WriteLine(result);

小数計算では、次のように使い分けるとよいでしょう。

計算内容
測定値や平均値double
金額、料金、税金decimal
ゲームや描画など軽量な小数float

特に、請求額や残高などの金額をdoubleで計算すると、わずかな誤差がトラブルにつながることがあります。C#の引き算で小数を使うときは、誤差を許容できるかどうかを考えましょう。

8-4. 日付・時間の差分はTimeSpanで扱う

DateTime同士を引き算すると、結果はTimeSpanになります。

C#
DateTime start = new DateTime(2025, 1, 1);
DateTime end = new DateTime(2025, 1, 10);

TimeSpan diff = end - start;

日数を取得する場合は、Days を使います。

C#
Console.WriteLine(diff.Days);

合計時間を取得する場合は、TotalHours を使います。

C#
Console.WriteLine(diff.TotalHours);

TimeSpanでは、DaysHoursMinutes のような部分を表すプロパティと、TotalDaysTotalHoursTotalMinutes のような合計値を表すプロパティがあります。

C#
TimeSpan span = new TimeSpan(1, 2, 0, 0);

Console.WriteLine(span.Hours);
Console.WriteLine(span.TotalHours);

Hours は2、TotalHours は26になります。

日付・時間の引き算では、TimeSpanのプロパティの違いを理解しておくことが大切です。

8-5. 初心者が覚えておきたい引き算コードの書き方

C#初心者がまず覚えておきたい引き算の書き方は、次の5つです。

数値同士の基本的な引き算です。

C#
int result = 10 - 3;

変数同士の引き算です。

C#
int result = a - b;

変数の値を減らす書き方です。

C#
a = a - b;

複合代入演算子を使う書き方です。

C#
a -= b;

1だけ減らす書き方です。

C#
a--;

日付の差分を求める書き方です。

C#
TimeSpan diff = endDate - startDate;

Listの差分を求める書き方です。

C#
var diff = listA.Except(listB);

これらを使い分けられるようになると、C#の引き算に関する基本的な処理はかなり書けるようになります。

9. C#の引き算に関するよくある質問

9-1. C#で1だけ引くにはどう書けばよいですか?

C#で1だけ引くには、デクリメント演算子 -- を使います。

C#
int count = 10;

count--;

Console.WriteLine(count);

実行結果は次のとおりです。

C#
9

次のように書いても同じ意味です。

C#
count = count - 1;

または、複合代入演算子を使って次のようにも書けます。

C#
count -= 1;

単純に1だけ減らしたい場合は、count-- がよく使われます。ただし、式の中で使うと前置と後置の違いが出るため、初心者のうちは単独の行で使うと安全です。

9-2. C#で日付の差を日数で求めるにはどうすればよいですか?

DateTime同士を引き算して、TimeSpanのDaysプロパティを使います。

C#
DateTime start = new DateTime(2025, 1, 1);
DateTime end = new DateTime(2025, 1, 10);

TimeSpan diff = end - start;

Console.WriteLine(diff.Days);

実行結果は次のとおりです。

C#
9

時刻部分を無視して日付だけで差を求めたい場合は、Dateプロパティを使います。

C#
int days = (end.Date - start.Date).Days;

Console.WriteLine(days);

DateTimeには時刻も含まれるため、日付だけの差を求めたいのか、実際の経過時間を求めたいのかを意識しましょう。

9-3. doubleの引き算で誤差が出るのはなぜですか?

doubleは小数を2進数で近似して扱うため、すべての小数を正確に表現できるわけではありません。そのため、引き算の結果にわずかな誤差が出ることがあります。

C#
double result = 0.3 - 0.1;

Console.WriteLine(result);

期待する結果は0.2ですが、次のような結果になる場合があります。

C#
0.19999999999999998

これはC#の不具合ではなく、浮動小数点数の性質によるものです。

金額など、誤差を避けたい計算ではdecimalを使いましょう。

C#
decimal result = 0.3m - 0.1m;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
0.2

9-4. intとdoubleを引き算すると結果の型はどうなりますか?

intとdoubleを引き算すると、intがdoubleに変換され、結果はdoubleになります。

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

double result = a - b;

Console.WriteLine(result);

実行結果は次のとおりです。

C#
6.5

次のように、結果をintに直接代入することはできません。

C#
int result = a - b;

この場合はコンパイルエラーになります。intとして扱いたい場合は、明示的にキャストします。

C#
int result = (int)(a - b);

ただし、小数点以下は切り捨てられるため注意が必要です。

9-5. Listの差分を求めるにはどのメソッドを使えばよいですか?

Listの差分を求めるには、LINQの Except メソッドを使います。

C#
using System;
using System.Collections.Generic;
using System.Linq;

List<int> listA = new List<int> { 1, 2, 3, 4, 5 };
List<int> listB = new List<int> { 2, 4 };

var diff = listA.Except(listB);

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

実行結果は次のとおりです。

C#
1
3
5

listA.Except(listB) は、listAには存在するがlistBには存在しない要素を返します。

ただし、Exceptは集合の差分を求めるメソッドです。重複要素を個数として細かく扱いたい場合は、別のロジックが必要になることがあります。

まとめ

C#の引き算は、基本的には「-」演算子を使って書きます。

C#
int result = 10 - 3;

変数の値を減らす場合は、次のように書けます。

C#
a = a - b;

より短く書くなら、複合代入演算子 -= を使います。

C#
a -= b;

1だけ減らす場合は、デクリメント演算子 -- を使います。

C#
a--;

数値の引き算では、int、double、decimal、floatなどの型の違いを理解することが大切です。整数にはint、小数にはdouble、金額計算にはdecimalを使うと、トラブルを避けやすくなります。

日付や時間の引き算では、DateTime同士を引き算してTimeSpanを取得します。

C#
TimeSpan diff = endDate - startDate;

TimeSpanでは、DaysやHoursのような部分的な値と、TotalDaysやTotalHoursのような合計値の違いに注意しましょう。

Listや配列、文字列では、「-」演算子で直接引き算するのではなく、Remove、RemoveAll、Except、Replace、Removeなどのメソッドを使います。

C#の引き算でエラーを防ぐには、型をそろえること、nullを考慮すること、オーバーフローや小数誤差に注意すること、日付の時刻部分やタイムゾーンを意識することが重要です。

初心者のうちは、まず次の基本形をしっかり覚えておきましょう。

C#
int result = a - b;
a -= b;
a--;
TimeSpan diff = endDate - startDate;
var diffList = listA.Except(listB);

これらを理解しておけば、C#の引き算を使った数値計算、日付・時間の差分計算、リストの差分取得など、さまざまな処理に対応できるようになります。