【初心者向け】C#のToString()とは?意外と奥が深い文字列変換メソッドのすべて

C#

「C#のToString()ってよく出てくるけど、いまいちピンとこない…」
そんな疑問を持つ方は多いのではないでしょうか?

ToString()は、C#のすべてのオブジェクトが持っているメソッドで、その名の通り、オブジェクトを文字列に変換するための機能です。

この記事では、ToString()の基本的な使い方から、カスタマイズ方法、そしてよくある落とし穴まで、初心者にもわかりやすく丁寧に解説していきます。

スポンサーリンク

ToString()の基本構文と動作

最もシンプルな使い方

int num = 123;
string text = num.ToString();
Console.WriteLine(text);  // "123"

いろいろな型での使用例

// 整数
int integer = 42;
string intText = integer.ToString();  // "42"

// 小数
double decimal = 3.14159;
string decimalText = decimal.ToString();  // "3.14159"

// 真偽値
bool flag = true;
string boolText = flag.ToString();  // "True"

// 日付
DateTime today = DateTime.Now;
string dateText = today.ToString();  // "2025/06/03 14:30:45"

ToString()の仕組み

すべてのクラスはObjectクラスを継承

// これらは同じ意味
object obj = 123;
string result1 = obj.ToString();

int num = 123;
string result2 = num.ToString();

内部では何が起こっている?

  1. オブジェクトの型を確認
  2. その型に定義されたToString()メソッドを実行
  3. 文字列として返す

いつToString()を使うの?

コンソール出力

int score = 85;
Console.WriteLine("あなたの点数は" + score.ToString() + "点です");

ログ出力

int userId = 12345;
Log.Info("ユーザーID: " + userId.ToString() + "がログインしました");

UI表示(WinFormsやWPF)

int count = 10;
label1.Text = count.ToString();

数値のフォーマット指定

基本的なフォーマット指定

double price = 1234.567;

// 小数点以下の桁数を指定
string f2 = price.ToString("F2");  // "1234.57"
string f0 = price.ToString("F0");  // "1235"

// 3桁区切りのカンマ
string n2 = price.ToString("N2");  // "1,234.57"
string n0 = price.ToString("N0");  // "1,235"

// 通貨形式
string currency = price.ToString("C");  // "¥1,235"

よく使うフォーマット一覧

フォーマット説明例(1234.567)
"F2"小数点以下2桁1234.57
"F0"整数部分のみ1235
"N0"3桁区切り(整数)1,235
"N2"3桁区切り(小数2桁)1,234.57
"C"通貨形式¥1,235
"P"パーセント形式123,456.70%
"E2"指数形式1.23E+003

実際の使用例

// 商品価格の表示
double itemPrice = 1980;
Console.WriteLine($"価格: {itemPrice.ToString("C")}");  // "価格: ¥1,980"

// 割引率の表示
double discountRate = 0.15;
Console.WriteLine($"割引率: {discountRate.ToString("P0")}");  // "割引率: 15%"

// 統計データの表示
double average = 87.654321;
Console.WriteLine($"平均点: {average.ToString("F1")}");  // "平均点: 87.7"

ゼロ埋めの指定

int number = 5;

// ゼロ埋めで指定桁数にする
string d3 = number.ToString("D3");  // "005"
string d5 = number.ToString("D5");  // "00005"

// 16進数表示
string hex = number.ToString("X2");  // "05"
string hexUpper = number.ToString("X4");  // "0005"

日付・時刻のフォーマット指定

基本的な日付フォーマット

DateTime now = new DateTime(2025, 6, 3, 14, 30, 45);

// 年月日
string date1 = now.ToString("yyyy/MM/dd");  // "2025/06/03"
string date2 = now.ToString("yyyy-MM-dd");  // "2025-06-03"
string date3 = now.ToString("MM/dd/yyyy");  // "06/03/2025"

// 時刻
string time1 = now.ToString("HH:mm:ss");    // "14:30:45"
string time2 = now.ToString("hh:mm tt");    // "02:30 PM"

// 日付と時刻
string datetime1 = now.ToString("yyyy/MM/dd HH:mm:ss");  // "2025/06/03 14:30:45"
string datetime2 = now.ToString("MM/dd/yyyy hh:mm tt");  // "06/03/2025 02:30 PM"

よく使う日付フォーマット一覧

フォーマット説明
"yyyy"4桁の年2025
"MM"2桁の月06
"dd"2桁の日03
"HH"24時間形式の時14
"hh"12時間形式の時02
"mm"30
"ss"45
"tt"AM/PMPM
"ddd"曜日(短縮形)Tue
"dddd"曜日(完全形)Tuesday

実際の使用例

DateTime now = DateTime.Now;

// ログファイル名用
string logFileName = now.ToString("yyyyMMdd") + ".log";  // "20250603.log"

// ユーザー表示用
string userDisplay = now.ToString("yyyy年M月d日 H時m分");  // "2025年6月3日 14時30分"

// システム間のデータ交換用(ISO形式)
string isoFormat = now.ToString("yyyy-MM-ddTHH:mm:ss");  // "2025-06-03T14:30:45"

// 日本語の曜日表示
System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("ja-JP");
string japaneseDay = now.ToString("yyyy年M月d日(ddd)", culture);  // "2025年6月3日(火)"

カスタムクラスでのToString()のオーバーライド

基本的なオーバーライド

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }

    public override string ToString()
    {
        return $"商品名: {Name}, 価格: {Price:C}, カテゴリ: {Category}";
    }
}

使用例

Product product = new Product 
{ 
    Name = "りんご", 
    Price = 150, 
    Category = "果物" 
};

Console.WriteLine(product.ToString());
// 出力: 商品名: りんご, 価格: ¥150, カテゴリ: 果物

より実践的な例

顧客情報クラス

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime RegisterDate { get; set; }

    public override string ToString()
    {
        return $"[ID:{Id}] {Name} ({Email}) - 登録日: {RegisterDate:yyyy/MM/dd}";
    }
}

注文情報クラス

public class Order
{
    public int OrderId { get; set; }
    public Customer Customer { get; set; }
    public List<Product> Items { get; set; }
    public DateTime OrderDate { get; set; }

    public override string ToString()
    {
        decimal total = Items?.Sum(item => item.Price) ?? 0;
        int itemCount = Items?.Count ?? 0;
        
        return $"注文#{OrderId} - {Customer?.Name} - " +
               $"商品数: {itemCount}, 合計: {total:C} ({OrderDate:yyyy/MM/dd})";
    }
}

デバッグ時の活用

public class BankAccount
{
    public string AccountNumber { get; set; }
    public string OwnerName { get; set; }
    public decimal Balance { get; set; }

    public override string ToString()
    {
        // デバッグ時に便利な情報を含める
        return $"口座[{AccountNumber}] {OwnerName} 残高:{Balance:C}";
    }
}

// デバッグ時の確認が楽になる
BankAccount account = new BankAccount 
{ 
    AccountNumber = "1234567890", 
    OwnerName = "田中太郎", 
    Balance = 50000 
};

Console.WriteLine($"処理対象: {account}");
// 出力: 処理対象: 口座[1234567890] 田中太郎 残高:¥50,000

より高度なToString()の使い方

IFormattableインターフェースの実装

public class Temperature : IFormattable
{
    public double Celsius { get; set; }

    public override string ToString()
    {
        return ToString("C", null);
    }

    public string ToString(string format)
    {
        return ToString(format, null);
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (string.IsNullOrEmpty(format)) format = "C";
        
        switch (format.ToUpperInvariant())
        {
            case "C":
                return $"{Celsius:F1}°C";
            case "F":
                return $"{(Celsius * 9.0 / 5.0) + 32:F1}°F";
            case "K":
                return $"{Celsius + 273.15:F1}K";
            default:
                throw new FormatException($"'{format}' は無効なフォーマットです");
        }
    }
}

使用例

Temperature temp = new Temperature { Celsius = 25.0 };

Console.WriteLine(temp.ToString());      // "25.0°C"
Console.WriteLine(temp.ToString("F"));   // "77.0°F"
Console.WriteLine(temp.ToString("K"));   // "298.1K"

複雑なオブジェクトの文字列化

public class Report
{
    public string Title { get; set; }
    public DateTime GeneratedAt { get; set; }
    public List<string> Data { get; set; }
    public Dictionary<string, object> Metadata { get; set; }

    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine($"=== {Title} ===");
        sb.AppendLine($"生成日時: {GeneratedAt:yyyy/MM/dd HH:mm:ss}");
        sb.AppendLine($"データ件数: {Data?.Count ?? 0}");
        
        if (Metadata?.Any() == true)
        {
            sb.AppendLine("メタデータ:");
            foreach (var kvp in Metadata)
            {
                sb.AppendLine($"  {kvp.Key}: {kvp.Value}");
            }
        }
        
        return sb.ToString();
    }
}

よくある間違いと対処法

1. nullオブジェクトに対するToString()

問題のあるコード

Product product = null;
Console.WriteLine(product.ToString());  // ❌ NullReferenceException

解決方法1: null チェック

Product product = null;
if (product != null)
{
    Console.WriteLine(product.ToString());
}
else
{
    Console.WriteLine("商品情報がありません");
}

解決方法2: null条件演算子

Product product = null;
Console.WriteLine(product?.ToString() ?? "商品情報がありません");

解決方法3: 拡張メソッド

public static class ObjectExtensions
{
    public static string ToStringSafe(this object obj, string defaultValue = "")
    {
        return obj?.ToString() ?? defaultValue;
    }
}

// 使用例
Product product = null;
Console.WriteLine(product.ToStringSafe("商品なし"));

2. 不正なフォーマット文字列

問題のあるコード

int value = 1000;
Console.WriteLine(value.ToString("Z"));  // ❌ FormatException

解決方法: try-catch で例外処理

int value = 1000;
string format = "Z";
try
{
    string result = value.ToString(format);
    Console.WriteLine(result);
}
catch (FormatException)
{
    Console.WriteLine($"無効なフォーマット '{format}' です。標準形式で表示: {value}");
}

3. 文化・地域設定の違い

問題:異なる地域での表示が変わる

double price = 1234.56;
DateTime date = new DateTime(2025, 6, 3);

// システムの地域設定によって結果が変わる
Console.WriteLine(price.ToString("C"));  // 日本: ¥1,235, アメリカ: $1,234.56
Console.WriteLine(date.ToString("d"));   // 日本: 2025/06/03, アメリカ: 6/3/2025

解決方法: CultureInfo を明示的に指定

using System.Globalization;

double price = 1234.56;
DateTime date = new DateTime(2025, 6, 3);

// 日本の形式で統一
CultureInfo jpCulture = new CultureInfo("ja-JP");
Console.WriteLine(price.ToString("C", jpCulture));  // ¥1,235
Console.WriteLine(date.ToString("d", jpCulture));   // 2025/06/03

// アメリカの形式で統一
CultureInfo usCulture = new CultureInfo("en-US");
Console.WriteLine(price.ToString("C", usCulture));  // $1,234.56
Console.WriteLine(date.ToString("d", usCulture));   // 6/3/2025

ToString()とその他の文字列化手法の比較

文字列補間(String Interpolation)との使い分け

int age = 25;
string name = "田中";

// ToString()を明示的に使用
string message1 = "私の名前は" + name + "で、年齢は" + age.ToString() + "歳です";

// 文字列補間(推奨)
string message2 = $"私の名前は{name}で、年齢は{age}歳です";

// String.Format
string message3 = String.Format("私の名前は{0}で、年齢は{1}歳です", name, age);

それぞれの特徴

方法メリットデメリット使用場面
ToString()明示的、カスタマイズ可能冗長になりがちカスタムフォーマットが必要
文字列補間 ($””)読みやすい、簡潔.NET Framework 4.6以降一般的な文字列作成
String.Format多言語対応しやすい可読性が低いローカライゼーション

パフォーマンスの考慮

StringBuilder との使い分け

小規模な文字列結合

// これで十分
string result = $"{user.Name} ({user.Age}歳) - {user.Email}";

大量の文字列結合

// StringBuilderを使用(高速)
var sb = new StringBuilder();
foreach (var item in largeList)
{
    sb.AppendLine(item.ToString());
}
string result = sb.ToString();

キャッシュの活用

public class Product
{
    private string _cachedString = null;
    
    public string Name { get; set; }
    public decimal Price { get; set; }

    public override string ToString()
    {
        // 値が変更されていない場合はキャッシュを返す
        if (_cachedString == null)
        {
            _cachedString = $"{Name} - {Price:C}";
        }
        return _cachedString;
    }
    
    // プロパティ変更時にキャッシュをクリア
    private void ClearCache()
    {
        _cachedString = null;
    }
}

実際の開発での活用例

ログ出力

public class Logger
{
    public static void LogInfo(object obj)
    {
        string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        Console.WriteLine($"[INFO] {timestamp}: {obj?.ToString() ?? "null"}");
    }
}

// 使用例
Product product = new Product { Name = "りんご", Price = 150 };
Logger.LogInfo(product);
// 出力: [INFO] 2025-06-03 14:30:45: 商品名: りんご, 価格: ¥150

デバッグ支援

public static class DebugHelper
{
    public static void PrintObject(object obj, string label = "Object")
    {
        Console.WriteLine($"=== {label} ===");
        if (obj == null)
        {
            Console.WriteLine("null");
        }
        else
        {
            Console.WriteLine($"Type: {obj.GetType().Name}");
            Console.WriteLine($"Value: {obj}");
        }
        Console.WriteLine();
    }
}

API レスポンス

public class ApiResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public object Data { get; set; }

    public override string ToString()
    {
        return $"ApiResponse [Success: {Success}, Message: '{Message}', Data: {Data?.ToString() ?? "null"}]";
    }
}

まとめ

C#のToString()メソッドは、単なる文字列変換以上の価値を持つ強力な機能です。

重要なポイント

  • すべてのオブジェクトがToString()メソッドを持っている
  • フォーマット指定で数値や日付を見やすく表示できる
  • オーバーライドでカスタムクラスの表示内容をカスタマイズ可能
  • null安全性やエラーハンドリングが重要

使い方のコツ

  • デバッグ時にわかりやすい文字列になるようオーバーライドする
  • 適切なフォーマット指定でユーザビリティを向上させる
  • null チェックやエラーハンドリングを忘れない
  • パフォーマンスが重要な場面では StringBuilder の使用を検討

よく使う場面

  • ログ出力とデバッグ
  • ユーザーへの表示
  • ファイル名や識別子の生成
  • API レスポンスの文字列化

コメント

タイトルとURLをコピーしました