C# decimal.TryParseの使い方|数値変換エラーを防ぐ基本・失敗原因・実践例

はじめに

C#で文字列を数値に変換するとき、入力値が常に正しい形式とは限りません。フォーム入力、CSV、APIレスポンス、設定ファイルなどから取得した値には、空文字、カンマ、通貨記号、全角文字、単位付き文字列などが混ざることがあります。

このような場面で便利なのがdecimal.TryParseです。decimal.TryParseを使うと、文字列をdecimal型へ安全に変換し、変換できたかどうかをboolで判定できます。Parseのように変換失敗時に例外が発生しないため、ユーザー入力や外部データの処理に向いています。

この記事では、C#のdecimal.TryParseの基本的な使い方から、変換に失敗する原因、NumberStylesCultureInfoを使った実践的な変換方法、金額チェックやCSV読み込みでの活用例まで詳しく解説します。

1. C#のdecimal.TryParseとは

1-1. decimal.TryParseの役割

decimal.TryParseは、文字列をdecimal型の数値に変換できるかを試すためのメソッドです。

たとえば、次のような文字列をdecimal型に変換できます。

C#
string text = "123.45";

bool success = decimal.TryParse(text, out decimal value);

Console.WriteLine(success); // True
Console.WriteLine(value); // 123.45

decimal.TryParseは、変換に成功するとtrueを返し、変換後の値をout引数に格納します。変換に失敗した場合はfalseを返します。

1-2. 文字列をdecimal型に安全に変換できる理由

decimal.TryParseが安全だといわれる理由は、変換に失敗しても例外を投げないからです。

たとえば、"abc"のような文字列は数値に変換できません。しかしdecimal.TryParseを使えば、プログラムを停止させずに失敗を判定できます。

C#
string text = "abc";

bool success = decimal.TryParse(text, out decimal value);

if (success)
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine("数値に変換できませんでした。");
}

ユーザーが入力する値や外部ファイルの値は、常に正しいとは限りません。そのため、失敗する可能性がある数値変換ではdecimal.TryParseを使うのが基本です。

1-3. Parse・Convert.ToDecimalとの違い

C#で文字列をdecimalに変換する方法には、主に次の3つがあります。

C#
decimal value1 = decimal.Parse("123.45");
decimal value2 = Convert.ToDecimal("123.45");

bool success = decimal.TryParse("123.45", out decimal value3);

decimal.Parseは、文字列をdecimalに変換します。ただし、変換できない文字列を渡すと例外が発生します。

C#
decimal value = decimal.Parse("abc"); // 例外が発生する

Convert.ToDecimalも変換できない値では例外が発生します。ただし、nullを渡した場合は0として扱われる点がdecimal.Parseとは異なります。

C#
decimal value = Convert.ToDecimal(null); // 0

一方、decimal.TryParseは変換に失敗しても例外を投げず、falseを返します。

C#
bool success = decimal.TryParse("abc", out decimal value);
// success は false

そのため、入力値が不確実な場合はdecimal.TryParseを使うのが安全です。

1-4. decimal型が金額・小数計算で使われる理由

decimal型は、金額や税率などの計算でよく使われます。理由は、doublefloatに比べて10進数の計算に向いているからです。

たとえば、金額計算では小数点以下の誤差が問題になることがあります。doubleは2進浮動小数点数であるため、10進数を正確に表現できない場合があります。

C#
double d = 0.1 + 0.2;
Console.WriteLine(d); // 0.30000000000000004 のような結果になることがある

一方、decimalは10進数の計算に適しているため、金額、単価、税率、請求額などの処理でよく使われます。

C#
decimal price = 0.1m + 0.2m;
Console.WriteLine(price); // 0.3

C#で金額や小数を扱う場合は、decimal.TryParseで安全に変換してから計算するのが実務ではよく使われる方法です。

2. decimal.TryParseの基本構文と戻り値

2-1. 基本構文

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

C#
bool success = decimal.TryParse(stringValue, out decimal result);

第1引数には変換したい文字列を指定します。第2引数には、変換後の値を受け取るdecimal型の変数を指定します。

C#
string input = "1000.5";

bool success = decimal.TryParse(input, out decimal result);

この場合、変換に成功すればsuccesstrueになり、resultには1000.5が入ります。

2-2. boolの戻り値で成功・失敗を判定する

decimal.TryParseの戻り値はboolです。

変換に成功した場合はtrue、失敗した場合はfalseを返します。

C#
string input = "123.45";

if (decimal.TryParse(input, out decimal value))
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine("変換失敗");
}

このようにif文と組み合わせることで、変換できた場合だけ計算や登録処理を実行できます。

2-3. out引数で変換後の値を受け取る

decimal.TryParseでは、変換後の値をout引数で受け取ります。

C#
string input = "500.25";
decimal result;

bool success = decimal.TryParse(input, out result);

Console.WriteLine(result);

out引数を使うため、メソッド呼び出し前に変数へ値を代入しておく必要はありません。TryParseの実行後に、成功時は変換後の値が入ります。

2-4. out varを使った簡潔な書き方

C#では、out varを使って変数宣言を簡潔に書けます。

C#
string input = "1200.75";

if (decimal.TryParse(input, out var amount))
{
Console.WriteLine($"金額: {amount}");
}

この書き方では、amountの型はコンパイラによってdecimalと推論されます。

明示的に型を書きたい場合は、次のようにも書けます。

C#
if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine(amount);
}

どちらもよく使われますが、シンプルに書きたい場合はout varが便利です。

2-5. 変換に失敗したときのresultの値

decimal.TryParseで変換に失敗した場合、out引数には0が入ります。

C#
string input = "abc";

bool success = decimal.TryParse(input, out decimal result);

Console.WriteLine(success); // False
Console.WriteLine(result); // 0

ただし、ここで注意が必要です。

result0だからといって、入力値が本当に"0"だったとは限りません。変換に失敗した場合も0になるため、必ず戻り値のboolを確認する必要があります。

C#
if (!decimal.TryParse(input, out decimal value))
{
Console.WriteLine("変換できませんでした。");
return;
}

Console.WriteLine($"変換後の値: {value}");

decimal.TryParseでは、resultの値だけで成功・失敗を判断しないようにしましょう。

3. decimal.TryParseの基本的な使い方

3-1. 数値文字列をdecimalに変換する例

もっとも基本的な例は、数値の文字列をdecimalに変換する処理です。

C#
string input = "123.45";

bool success = decimal.TryParse(input, out decimal number);

if (success)
{
Console.WriteLine(number);
}

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

C#
123.45

"123"のような整数文字列もdecimalに変換できます。

C#
string input = "123";

decimal.TryParse(input, out decimal number);

Console.WriteLine(number); // 123

3-2. if文で変換成功時だけ処理する例

実務では、変換に成功した場合だけ計算処理を行うことが多いです。

C#
string input = "1500.50";

if (decimal.TryParse(input, out decimal price))
{
decimal tax = price * 0.1m;
decimal total = price + tax;

Console.WriteLine($"税額: {tax}");
Console.WriteLine($"税込金額: {total}");
}

このように書けば、inputが不正な値だった場合に計算処理へ進まないため、エラーを防ぎやすくなります。

3-3. 変換失敗時にエラーメッセージを表示する例

ユーザー入力を扱う場合は、変換できなかったときにわかりやすいエラーメッセージを表示すると親切です。

C#
string input = "abc";

if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine($"入力された金額: {amount}");
}
else
{
Console.WriteLine("金額は数値で入力してください。");
}

入力欄のチェック、注文フォーム、会計処理などでは、TryParseで検証してから処理を進めることが重要です。

3-4. ユーザー入力を安全に数値変換する例

コンソールアプリでユーザーから金額を入力してもらう例です。

C#
Console.Write("金額を入力してください: ");
string? input = Console.ReadLine();

if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine($"入力された金額は {amount} 円です。");
}
else
{
Console.WriteLine("正しい金額を入力してください。");
}

Console.ReadLine()の戻り値はstring?です。入力が取得できない場合はnullになる可能性がありますが、decimal.TryParsenullを渡しても例外を投げず、falseを返します。

3-5. 空文字・nullを受け取った場合の動き

decimal.TryParseに空文字やnullを渡すと、変換に失敗してfalseが返ります。

C#
string empty = "";
string? nullValue = null;

bool result1 = decimal.TryParse(empty, out decimal value1);
bool result2 = decimal.TryParse(nullValue, out decimal value2);

Console.WriteLine(result1); // False
Console.WriteLine(value1); // 0

Console.WriteLine(result2); // False
Console.WriteLine(value2); // 0

空文字やnullは数値として扱えないため、変換結果は失敗になります。

ユーザー入力では、string.IsNullOrWhiteSpaceと組み合わせると、より丁寧にチェックできます。

C#
if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("値を入力してください。");
}
else if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine($"金額: {amount}");
}
else
{
Console.WriteLine("数値として正しくありません。");
}

4. decimal.TryParseで変換に失敗する主な原因

4-1. 数字以外の文字が含まれている

decimal.TryParseは、数値として解釈できない文字が含まれていると失敗します。

C#
decimal.TryParse("abc", out _);      // False
decimal.TryParse("123abc", out _); // False
decimal.TryParse("1,000円", out _); // 条件によっては False

特に、単位や説明文が混ざっている文字列はそのままでは変換できません。

C#
string input = "100kg";

bool success = decimal.TryParse(input, out decimal value);

Console.WriteLine(success); // False

このような場合は、変換前に不要な文字を取り除くか、入力ルールを明確にする必要があります。

4-2. 空文字・スペースだけの文字列を渡している

空文字やスペースだけの文字列は数値ではないため、変換に失敗します。

C#
decimal.TryParse("", out _);      // False
decimal.TryParse(" ", out _); // False

入力チェックでは、まず空欄かどうかを判定すると原因を切り分けやすくなります。

C#
if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("入力が空です。");
}
else if (!decimal.TryParse(input, out decimal value))
{
Console.WriteLine("数値形式が正しくありません。");
}

4-3. decimalの範囲を超えた値を渡している

decimal型には扱える範囲があります。非常に大きすぎる値や小さすぎる値を渡すと、decimal.TryParseは失敗します。

C#
string input = "999999999999999999999999999999999999999999";

bool success = decimal.TryParse(input, out decimal value);

Console.WriteLine(success); // False

decimalは金額や小数計算に向いていますが、無限に大きな数値を扱えるわけではありません。外部データを取り込む場合は、桁数のチェックも重要です。

4-4. 小数点やカンマの形式が環境に合っていない

小数点や桁区切りの記号は、実行環境のカルチャによって解釈が変わることがあります。

日本語環境や英語環境では、小数点に.、桁区切りに,を使うことが一般的です。

C#
decimal.TryParse("1,234.56", out decimal value);

一方、ヨーロッパの一部のカルチャでは、小数点に,、桁区切りに.を使う場合があります。

C#
// 文化圏によっては 1234.56 の意味になる
"1.234,56"

このような形式の違いがある場合は、CultureInfoを明示して変換する必要があります。

4-5. 全角数字・通貨記号・単位付き文字列を渡している

全角数字、通貨記号、単位付き文字列は、期待どおりに変換できないことがあります。

C#
decimal.TryParse("123.45", out _); // 環境や文字により失敗する可能性がある
decimal.TryParse("¥1,000", out _); // 通貨記号の扱いには指定が必要
decimal.TryParse("100円", out _); // False になりやすい
decimal.TryParse("10kg", out _); // False

通貨記号付きの金額を扱う場合は、NumberStyles.Currencyを指定します。

C#
using System.Globalization;

string input = "¥1,000";

bool success = decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("ja-JP"),
out decimal amount
);

単位付き文字列の場合は、単位を削除してから変換するか、入力形式を分ける設計にするのが安全です。

4-6. 失敗原因を切り分けるチェックポイント

decimal.TryParseが失敗する場合は、次の順番で確認すると原因を見つけやすくなります。

まず、値がnullまたは空文字ではないかを確認します。

C#
if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("入力が空です。");
}

次に、数字以外の文字が混ざっていないかを確認します。

C#
Console.WriteLine(input);

表示すると、見た目ではわかりにくいスペースや記号が含まれていることに気づける場合があります。

さらに、カンマ、小数点、通貨記号、カルチャの違いを確認します。

C#
decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

CSVやAPIのように形式が決まっているデータでは、CultureInfo.InvariantCultureを使って環境差をなくすことがよくあります。

5. NumberStylesとCultureInfoを使った実践的な変換

5-1. NumberStylesを指定する必要があるケース

NumberStylesは、どのような形式の文字列を数値として許可するかを指定する列挙型です。

通常のdecimal.TryParse(input, out value)でも基本的な数値変換はできますが、次のようなケースではNumberStylesを指定した方が安全です。

C#
using System.Globalization;

string input = "1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

NumberStyles.Numberは、符号、小数点、桁区切り、前後の空白などを許可する一般的な数値形式です。

通貨記号を含めたい場合は、NumberStyles.Currencyを使います。

C#
string input = "$1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("en-US"),
out decimal value
);

5-2. カンマ区切りの数値を変換する方法

カンマ区切りの数値を変換する場合は、NumberStyles.Numberを指定します。

C#
using System.Globalization;

string input = "1,234.56";

if (decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal amount))
{
Console.WriteLine(amount); // 1234.56
}

日本語環境でよく使われる形式なら、ja-JPを指定しても構いません。

C#
using System.Globalization;

string input = "1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.GetCultureInfo("ja-JP"),
out decimal amount
);

入力データの形式が固定されている場合は、現在のPC環境に依存させず、明示的にCultureInfoを指定するのがおすすめです。

5-3. 通貨記号付きの文字列を変換する方法

通貨記号付きの文字列を変換するには、NumberStyles.Currencyを使います。

C#
using System.Globalization;

string input = "¥1,234";

bool success = decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("ja-JP"),
out decimal amount
);

if (success)
{
Console.WriteLine(amount); // 1234
}

米ドル表記の場合は、en-USなどのカルチャを指定します。

C#
using System.Globalization;

string input = "$1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("en-US"),
out decimal amount
);

Console.WriteLine(success);
Console.WriteLine(amount);

通貨記号はカルチャと関係が深いため、¥を扱うならja-JP$を扱うならen-USのように、入力形式に合ったカルチャを指定することが大切です。

5-4. CultureInfoで小数点・桁区切りの違いに対応する

CultureInfoを使うと、小数点や桁区切りの違いに対応できます。

たとえば、英語圏では次のような形式が一般的です。

C#
"1,234.56"

この場合、カンマが桁区切り、ピリオドが小数点です。

C#
using System.Globalization;

string input = "1,234.56";

decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.GetCultureInfo("en-US"),
out decimal value
);

Console.WriteLine(value); // 1234.56

一方、ドイツ語圏などでは次のような形式が使われます。

C#
"1.234,56"

この場合、ピリオドが桁区切り、カンマが小数点です。

C#
using System.Globalization;

string input = "1.234,56";

decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.GetCultureInfo("de-DE"),
out decimal value
);

Console.WriteLine(value); // 1234.56

同じ文字列でもカルチャによって解釈が変わるため、多言語対応や海外向けサービスではCultureInfoの指定が重要です。

5-5. InvariantCultureを使うべきケース

CultureInfo.InvariantCultureは、特定の国や地域に依存しないカルチャです。

API、CSV、JSON、設定ファイルなど、プログラム間でやり取りするデータではInvariantCultureを使うことがよくあります。

C#
using System.Globalization;

string input = "1234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

InvariantCultureを使うと、実行環境の言語設定に左右されにくくなります。

たとえば、CSVの数値形式を必ず1234.56のように決めている場合、次のように書くと安定します。

C#
decimal.TryParse(
csvValue,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal amount
);

外部システムとの連携では、現在のカルチャに任せるよりも、データ仕様に合わせたカルチャを明示する方が安全です。

5-6. 日本語環境と英語環境で結果が変わる例

decimal.TryParse(input, out value)のようにカルチャを指定しない場合、現在のカルチャが使われます。

そのため、実行環境によって結果が変わる可能性があります。

C#
string input = "1,234.56";

bool success = decimal.TryParse(input, out decimal value);

日本語環境や英語環境では成功しやすい形式ですが、別のカルチャでは期待どおりに解釈されない場合があります。

環境差を避けたい場合は、次のように明示します。

C#
using System.Globalization;

string input = "1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.GetCultureInfo("en-US"),
out decimal value
);

また、固定形式のデータであればInvariantCultureを使います。

C#
bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

ユーザーが画面で入力する値ならユーザーの地域設定に合わせ、システム同士でやり取りする値ならInvariantCultureを使う、という使い分けが実践的です。

6. decimal.TryParseを使った実践例

6-1. フォーム入力の金額チェック

Webフォームやデスクトップアプリで金額を入力してもらう場合、decimal.TryParseで検証してから保存や計算を行います。

C#
string input = "2500";

if (!decimal.TryParse(input, out decimal amount))
{
Console.WriteLine("金額は数値で入力してください。");
return;
}

if (amount < 0)
{
Console.WriteLine("金額は0以上で入力してください。");
return;
}

Console.WriteLine($"登録する金額: {amount}");

decimal.TryParseは数値形式のチェックを行いますが、業務上のルールまでは判定しません。

たとえば、「金額は0以上」「上限は100万円まで」「小数点以下は2桁まで」といった条件は、別途チェックする必要があります。

C#
if (amount > 1_000_000m)
{
Console.WriteLine("金額は100万円以下で入力してください。");
}

6-2. CSVから読み込んだ小数値の変換

CSVから小数値を読み込む場合は、カルチャを明示するのがおすすめです。

C#
using System.Globalization;

string csvValue = "1234.56";

if (decimal.TryParse(
csvValue,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal amount))
{
Console.WriteLine($"読み込み成功: {amount}");
}
else
{
Console.WriteLine($"数値変換に失敗しました: {csvValue}");
}

CSVの形式が1,234.56のようにカンマ区切りを含む場合も、NumberStyles.Numberを指定しておくと対応しやすくなります。

C#
string csvValue = "1,234.56";

decimal.TryParse(
csvValue,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal amount
);

CSVを扱う場合は、ファイル仕様として小数点や桁区切りの形式を決めておくことが重要です。

6-3. APIや設定ファイルから取得した文字列の変換

APIや設定ファイルから取得した文字列をdecimalに変換する場合も、TryParseを使うと安全です。

C#
using System.Globalization;

string rateText = "0.10";

if (decimal.TryParse(
rateText,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal taxRate))
{
Console.WriteLine($"税率: {taxRate}");
}
else
{
Console.WriteLine("税率の設定値が不正です。");
}

設定ファイルの値が間違っていると、アプリケーション全体の動作に影響することがあります。Parseで例外を発生させるよりも、TryParseで検証し、失敗時にログ出力やデフォルト値の適用を行う設計が実用的です。

C#
decimal taxRate;

if (!decimal.TryParse(
rateText,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out taxRate))
{
taxRate = 0.10m;
Console.WriteLine("税率の設定が不正なため、デフォルト値を使用します。");
}

6-4. 税率・単価・数量を使った計算前の入力検証

単価、数量、税率を使って合計金額を計算する例です。

C#
using System.Globalization;

string priceText = "1200.50";
string quantityText = "3";
string taxRateText = "0.10";

if (!decimal.TryParse(priceText, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal price))
{
Console.WriteLine("単価が不正です。");
return;
}

if (!decimal.TryParse(quantityText, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal quantity))
{
Console.WriteLine("数量が不正です。");
return;
}

if (!decimal.TryParse(taxRateText, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal taxRate))
{
Console.WriteLine("税率が不正です。");
return;
}

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

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

計算前にすべての入力値を検証することで、不正なデータによる計算ミスを防げます。

また、数量が整数であるべき場合は、decimal.TryParseではなくint.TryParseを使う方が適している場合もあります。

6-5. 変換できない値をログに残す実装例

外部ファイルやAPIデータを処理する場合、変換に失敗した値をログに残すと原因調査がしやすくなります。

C#
using System.Globalization;

string[] values = { "100.5", "abc", "200.75", "", "1,234.56" };

foreach (string text in values)
{
if (decimal.TryParse(
text,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value))
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine($"変換失敗: '{text}'");
}
}

実務では、ログ出力用のメソッドを用意しておくと便利です。

C#
static bool TryConvertAmount(string? text, out decimal amount)
{
if (decimal.TryParse(
text,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out amount))
{
return true;
}

Console.WriteLine($"金額の変換に失敗しました。入力値: '{text}'");
return false;
}

呼び出し側は次のように書けます。

C#
if (TryConvertAmount("abc", out decimal amount))
{
Console.WriteLine(amount);
}

不正な値を無視するのではなく、どの値が失敗したのかを記録することで、データ品質の改善にもつながります。

7. decimal.TryParseを使うときの注意点

7-1. 変換失敗時に0として扱う危険性

decimal.TryParseで変換に失敗すると、out引数の値は0になります。しかし、失敗した値をそのまま0として扱うのは危険です。

C#
decimal.TryParse("abc", out decimal amount);

Console.WriteLine(amount); // 0

このコードでは、"abc"が入力されたにもかかわらず、amount0になります。

もし戻り値を確認せずに計算へ進むと、不正な入力が0円として処理されてしまいます。

C#
if (!decimal.TryParse(input, out decimal amount))
{
Console.WriteLine("金額が不正です。");
return;
}

TryParseを使うときは、必ず戻り値のtrueまたはfalseを確認しましょう。

7-2. null許容decimalに代入したい場合

変換できない場合にnullとして扱いたい場合は、decimal?を使います。

C#
string input = "123.45";

decimal? amount = decimal.TryParse(input, out decimal value)
? value
: null;

Console.WriteLine(amount);

メソッド化すると、より使いやすくなります。

C#
static decimal? ToNullableDecimal(string? text)
{
return decimal.TryParse(text, out decimal value)
? value
: null;
}

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

C#
decimal? amount1 = ToNullableDecimal("100.5");
decimal? amount2 = ToNullableDecimal("abc");

Console.WriteLine(amount1); // 100.5
Console.WriteLine(amount2); // null

入力が任意項目の場合や、未入力と0を区別したい場合はdecimal?が便利です。

7-3. double.TryParseやint.TryParseとの違い

decimal.TryParseと似たメソッドに、double.TryParseint.TryParseがあります。

int.TryParseは整数への変換に使います。

C#
int.TryParse("123", out int count);

小数を渡すと失敗します。

C#
bool success = int.TryParse("123.45", out int count);
// success は false

double.TryParseは小数を扱えますが、浮動小数点数であるため、金額計算では誤差に注意が必要です。

C#
double.TryParse("123.45", out double doubleValue);
decimal.TryParse("123.45", out decimal decimalValue);

金額、税率、単価など正確な10進数計算が重要な場合はdecimal.TryParseを使うのが一般的です。

一方、科学計算や非常に大きい・小さい値を扱う場合はdoubleが向いていることもあります。用途に応じて使い分けましょう。

7-4. 例外処理ではなくTryParseを使うべき場面

変換に失敗する可能性が普通にある場面では、try-catchdecimal.Parseを囲むよりもdecimal.TryParseを使う方が自然です。

避けたい例は次のようなコードです。

C#
try
{
decimal amount = decimal.Parse(input);
Console.WriteLine(amount);
}
catch
{
Console.WriteLine("変換できませんでした。");
}

ユーザー入力やCSV読み込みでは、変換できない値が含まれることは想定内です。このような通常の分岐には例外処理ではなくTryParseを使う方が読みやすくなります。

C#
if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine(amount);
}
else
{
Console.WriteLine("変換できませんでした。");
}

例外処理は、想定外のエラーに使うものです。入力値の検証にはTryParseを使うのが適しています。

7-5. 入力値の前処理をどこまで行うべきか

decimal.TryParseの前に、入力値を加工することがあります。

たとえば、前後の空白を削除する処理です。

C#
string input = " 123.45 ";
string trimmed = input.Trim();

decimal.TryParse(trimmed, out decimal value);

通貨記号や単位を取り除くこともできます。

C#
string input = "100円";
string normalized = input.Replace("円", "");

decimal.TryParse(normalized, out decimal amount);

ただし、前処理をやりすぎると、本来はエラーにすべき値まで変換できてしまう可能性があります。

たとえば、"1a2b3"から文字だけを取り除いて"123"として扱うと、ユーザーの入力ミスに気づけません。

入力値の前処理は、次のように方針を決めておくと安全です。

ユーザー入力では、基本的に不要な空白の削除程度にとどめます。通貨記号やカンマを許可する場合は、画面上に入力ルールを明示します。CSVやAPIでは、仕様に合わない値はエラーとして扱い、ログに残します。

decimal.TryParseは便利ですが、どの形式を許可するかはアプリケーション側で明確に決めることが重要です。

8. decimal.TryParseのよくある質問

8-1. decimal.TryParseは例外を投げるのか

通常、decimal.TryParseは変換に失敗しても例外を投げません。変換できない場合はfalseを返します。

C#
bool success = decimal.TryParse("abc", out decimal value);

Console.WriteLine(success); // False
Console.WriteLine(value); // 0

そのため、入力値が不確実な場面でも安全に使えます。

ただし、引数の指定方法が不正な場合など、通常の数値変換失敗とは別の問題では例外が発生する可能性があります。一般的な文字列変換の失敗については、falseで判定できると考えて問題ありません。

8-2. 小数点以下の桁数はどう扱われるのか

decimal.TryParseは、小数点以下を含む文字列をdecimalとして変換できます。

C#
decimal.TryParse("123.456", out decimal value);

Console.WriteLine(value); // 123.456

ただし、decimal型が扱える精度を超えるような値では、丸めが発生したり、変換に失敗したりする場合があります。

金額計算で小数点以下2桁にしたい場合は、変換後に桁数をチェックまたは丸めます。

C#
decimal rounded = Math.Round(value, 2);

Console.WriteLine(rounded);

小数点以下の桁数を入力時点で制限したい場合は、文字列の形式チェックや正規表現を組み合わせる方法もあります。

8-3. カンマ区切りの数値は変換できるのか

カンマ区切りの数値は、形式とカルチャが合っていれば変換できます。

C#
using System.Globalization;

string input = "1,234.56";

bool success = decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

Console.WriteLine(success); // True
Console.WriteLine(value); // 1234.56

ただし、カンマが小数点として使われる地域もあります。そのため、海外形式のデータを扱う場合はCultureInfoを明示することが大切です。

8-4. 通貨記号付きの金額は変換できるのか

通貨記号付きの金額は、NumberStyles.Currencyと適切なCultureInfoを指定すれば変換できます。

C#
using System.Globalization;

string input = "¥1,234";

bool success = decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("ja-JP"),
out decimal amount
);

Console.WriteLine(success); // True
Console.WriteLine(amount); // 1234

ドル表記なら、次のようにen-USを使います。

C#
string input = "$1,234.56";

decimal.TryParse(
input,
NumberStyles.Currency,
CultureInfo.GetCultureInfo("en-US"),
out decimal amount
);

通貨記号を扱うときは、入力文字列とカルチャの組み合わせを一致させることが重要です。

8-5. ParseとTryParseはどちらを使うべきか

ユーザー入力、CSV、API、設定ファイルなど、変換に失敗する可能性がある値にはdecimal.TryParseを使うべきです。

C#
if (decimal.TryParse(input, out decimal amount))
{
Console.WriteLine(amount);
}
else
{
Console.WriteLine("数値ではありません。");
}

一方、必ず正しい数値文字列であることが保証されている場合は、decimal.Parseを使っても問題ありません。

C#
decimal amount = decimal.Parse("123.45");

ただし、実務では入力値が常に正しいとは限らないため、多くの場面でTryParseの方が安全です。

まとめ

decimal.TryParseは、文字列をdecimal型に安全に変換するための便利なメソッドです。変換に成功するとtrueを返し、変換後の値をout引数に格納します。失敗した場合はfalseを返すため、例外を発生させずに入力チェックを行えます。

基本的な使い方は次のとおりです。

C#
if (decimal.TryParse(input, out decimal value))
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine("変換失敗");
}

金額、税率、単価、小数値などを扱う場合は、doubleではなくdecimalを使うことで、10進数の計算に適した処理ができます。

また、カンマ区切りや通貨記号付きの文字列を扱う場合は、NumberStylesCultureInfoを指定することが重要です。

C#
using System.Globalization;

decimal.TryParse(
input,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);

decimal.TryParseを使うときは、変換失敗時のresult0になる点に注意し、必ず戻り値のboolを確認しましょう。ユーザー入力や外部データの変換では、ParseよりもTryParseを使うことで、数値変換エラーに強い安全なC#コードを書けます。