C#のToStringをoverrideする方法|基本構文から実践例・注意点まで解説

はじめに

C#でクラスや構造体を扱っていると、Console.WriteLineやログ出力、デバッグ画面などでオブジェクトの内容を文字列として確認したい場面がよくあります。そのときに重要になるのがToStringメソッドです。

ToStringは、オブジェクトを文字列として表現するためのメソッドです。C#ではすべての型が最終的にobject型を継承しているため、多くのクラスでToStringを利用できます。

ただし、自作クラスで何も設定しないままToStringを呼び出すと、期待したようなプロパティの値ではなく、型名だけが表示されることがあります。そこで使うのがoverrideです。

この記事では、C#でToStringoverrideする方法について、基本構文、実践例、注意点、よくある疑問まで解説します。

1. C#のToString overrideとは?検索ユーザーがまず知りたい基本

1-1. ToStringメソッドの役割

ToStringメソッドは、オブジェクトを文字列として表現するためのメソッドです。

たとえば、数値や日付には最初から分かりやすいToStringの動作が用意されています。

C#
int number = 123;
Console.WriteLine(number.ToString());

DateTime today = DateTime.Now;
Console.WriteLine(today.ToString());

このように、ToStringを使うと値を文字列として取得できます。

一方、自作クラスの場合は、どのプロパティをどのように文字列化するかをC#側が自動で判断できるわけではありません。そのため、自分でToStringの内容を定義する必要があります。

1-2. overrideする意味とメリット

overrideとは、親クラスで定義されているメソッドを、子クラス側で上書きして独自の処理に変更する仕組みです。

ToStringoverrideすると、自作クラスのオブジェクトを文字列として表示したときに、任意の内容を出力できます。

たとえば、次のようなメリットがあります。

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

public override string ToString()
{
return $"Person: Name={Name}, Age={Age}";
}
}

このようにしておくと、Personオブジェクトを表示したときに、名前や年齢が分かりやすく出力されます。

C#
var person = new Person { Name = "田中", Age = 30 };

Console.WriteLine(person);

出力例は次のとおりです。

Person: Name=田中, Age=30

ToStringoverrideしておくと、デバッグ、ログ、コンソール出力などでオブジェクトの状態を確認しやすくなります。

1-3. overrideしない場合のデフォルト出力

自作クラスでToStringoverrideしない場合、通常は型名が出力されます。

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

このクラスをそのまま表示してみます。

C#
var person = new Person { Name = "田中", Age = 30 };

Console.WriteLine(person.ToString());

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

Person

名前や年齢ではなく、クラス名だけが表示されます。名前空間が付いている場合は、次のように完全修飾名が表示されることもあります。

SampleApp.Models.Person

これではオブジェクトの中身が分かりにくいため、必要に応じてToStringoverrideします。

1-4. ToStringが呼び出される主な場面

ToStringは、明示的に呼び出したときだけでなく、文字列が必要な場面で自動的に呼び出されることがあります。

代表的な場面は次のとおりです。

C#
var person = new Person { Name = "田中", Age = 30 };

Console.WriteLine(person);
string message = "対象ユーザー: " + person;
string text = $"{person}";

Console.WriteLine(person)のように書いた場合でも、内部的にはperson.ToString()の結果が使われます。

そのため、ToStringを適切にoverrideしておくと、さまざまな場面で自然に分かりやすい文字列を出力できます。

2. C#でToStringをoverrideする基本構文

2-1. public override string ToString() の書き方

C#でToStringoverrideする基本構文は次のとおりです。

C#
public override string ToString()
{
return "表示したい文字列";
}

重要なのは、次の形で書くことです。

C#
public override string ToString()

publicはアクセス修飾子、overrideは親クラスのメソッドを上書きするためのキーワード、stringは戻り値の型、ToStringはメソッド名です。

ToStringの戻り値は必ずstringです。intboolなど、別の型を返すことはできません。

2-2. クラスでToStringをoverrideする最小コード例

最小限の例として、BookクラスでToStringoverrideしてみます。

C#
public class Book
{
public string Title { get; set; } = "";

public override string ToString()
{
return Title;
}
}

このクラスを使うと、次のように本のタイトルを直接表示できます。

C#
var book = new Book { Title = "C#入門" };

Console.WriteLine(book);

出力結果は次のとおりです。

C#入門

ToStringoverrideしていない場合は型名が表示されますが、overrideすることでオブジェクトを分かりやすい文字列として扱えます。

2-3. returnで表示したい文字列を返す考え方

ToStringでは、returnで表示したい文字列を返します。

C#
public override string ToString()
{
return $"Title={Title}";
}

複数のプロパティを含めたい場合は、文字列補間を使うと読みやすく書けます。

C#
public class Book
{
public string Title { get; set; } = "";
public string Author { get; set; } = "";
public int Price { get; set; }

public override string ToString()
{
return $"Book: Title={Title}, Author={Author}, Price={Price}";
}
}

ToStringの目的は、オブジェクトの状態を文字列で分かりやすく表すことです。どの情報を含めるべきかは、デバッグやログで何を確認したいかを基準に決めるとよいでしょう。

2-4. overrideできる理由とObject.ToStringとの関係

C#のすべてのクラスは、最終的にobject型を継承しています。そして、object型にはToStringメソッドが定義されています。

つまり、自作クラスでも最初からToStringを持っている状態です。

C#
public class Person
{
}

このような空のクラスでも、次のようにToStringを呼び出せます。

C#
var person = new Person();
Console.WriteLine(person.ToString());

これは、Personクラスがobjectから継承したToStringを利用しているためです。

object.ToStringvirtualメソッドとして定義されているため、派生クラス側でoverrideできます。その結果、自作クラスに合った文字列表現を定義できます。

3. ToString overrideの実践例

3-1. Personクラスで名前と年齢を返す例

まずは、よく使われるPersonクラスの例です。

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

public override string ToString()
{
return $"Person: Name={Name}, Age={Age}";
}
}

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

C#
var person = new Person
{
Name = "佐藤",
Age = 28
};

Console.WriteLine(person);

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

Person: Name=佐藤, Age=28

クラス名と主要なプロパティを含めることで、ログやデバッグ時に何のオブジェクトなのかが分かりやすくなります。

3-2. 商品クラスで商品名・価格・在庫を返す例

次に、商品情報を表すProductクラスの例です。

C#
public class Product
{
public string Name { get; set; } = "";
public decimal Price { get; set; }
public int Stock { get; set; }

public override string ToString()
{
return $"Product: Name={Name}, Price={Price}円, Stock={Stock}";
}
}

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

C#
var product = new Product
{
Name = "キーボード",
Price = 4980m,
Stock = 12
};

Console.WriteLine(product);

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

Product: Name=キーボード, Price=4980円, Stock=12

商品名、価格、在庫数を含めることで、商品オブジェクトの状態を一目で確認できます。

3-3. DateTimeや数値の書式を含めて出力する例

ToStringの中では、日付や数値の書式指定も利用できます。

C#
public class Order
{
public int OrderId { get; set; }
public DateTime OrderedAt { get; set; }
public decimal TotalAmount { get; set; }

public override string ToString()
{
return $"Order: OrderId={OrderId}, OrderedAt={OrderedAt:yyyy-MM-dd HH:mm:ss}, TotalAmount={TotalAmount:N0}円";
}
}

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

C#
var order = new Order
{
OrderId = 1001,
OrderedAt = new DateTime(2026, 6, 19, 10, 30, 0),
TotalAmount = 12500m
};

Console.WriteLine(order);

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

Order: OrderId=1001, OrderedAt=2026-06-19 10:30:00, TotalAmount=12,500円

DateTimeにはyyyy-MM-ddのような日付書式を指定できます。数値にはN0のような書式を使うことで、桁区切りを含めた表示ができます。

3-4. 文字列補間を使って読みやすく実装する例

C#では、文字列補間を使うとToStringを読みやすく実装できます。

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

public override string ToString()
{
return $"Customer: Id={Id}, Name={Name}, Email={Email}";
}
}

文字列連結で書くこともできます。

C#
public override string ToString()
{
return "Customer: Id=" + Id + ", Name=" + Name + ", Email=" + Email;
}

しかし、プロパティが増えると文字列連結は読みにくくなります。通常は、文字列補間を使ったほうがシンプルで保守しやすくなります。

4. ToString overrideを使うべき場面

4-1. デバッグ時にオブジェクトの中身を確認したい場合

ToStringoverrideしておくと、デバッグ時にオブジェクトの内容を把握しやすくなります。

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

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

public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}
}

デバッグ中にuser.ToString()の結果を見ることで、現在のユーザー情報をすぐに確認できます。

C#
var user = new User { Id = 1, Name = "山田" };
Console.WriteLine(user);

出力結果は次のとおりです。

User: Id=1, Name=山田

型名だけが表示されるよりも、実際の値が見えるほうが問題の原因を追いやすくなります。

4-2. ログ出力で状態を分かりやすく残したい場合

ログにオブジェクトの状態を残したい場合にも、ToStringoverrideは便利です。

C#
public class Payment
{
public int PaymentId { get; set; }
public decimal Amount { get; set; }
public string Status { get; set; } = "";

public override string ToString()
{
return $"Payment: PaymentId={PaymentId}, Amount={Amount}, Status={Status}";
}
}

ログ出力時に次のように使えます。

C#
var payment = new Payment
{
PaymentId = 501,
Amount = 3000m,
Status = "Completed"
};

Console.WriteLine($"決済処理完了: {payment}");

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

決済処理完了: Payment: PaymentId=501, Amount=3000, Status=Completed

ただし、ログに出してよい情報かどうかは必ず確認する必要があります。パスワード、トークン、個人情報などはToStringに含めるべきではありません。

4-3. Console.WriteLineで自然に表示したい場合

Console.WriteLineにオブジェクトを渡すと、ToStringの結果が表示されます。

C#
public class TaskItem
{
public string Title { get; set; } = "";
public bool IsCompleted { get; set; }

public override string ToString()
{
return $"TaskItem: Title={Title}, IsCompleted={IsCompleted}";
}
}

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

C#
var task = new TaskItem
{
Title = "資料作成",
IsCompleted = false
};

Console.WriteLine(task);

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

TaskItem: Title=資料作成, IsCompleted=False

Console.WriteLine(task.ToString())と明示的に書かなくても、自然に文字列として表示されます。

4-4. コレクション内の要素を見やすく表示したい場合

リストや配列などのコレクションに入っている要素を表示する場合にも、ToStringoverrideしておくと便利です。

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

public override string ToString()
{
return $"Category: Id={Id}, Name={Name}";
}
}

リストをループして表示します。

C#
var categories = new List<Category>
{
new Category { Id = 1, Name = "書籍" },
new Category { Id = 2, Name = "家電" },
new Category { Id = 3, Name = "食品" }
};

foreach (var category in categories)
{
Console.WriteLine(category);
}

出力結果は次のとおりです。

Category: Id=1, Name=書籍
Category: Id=2, Name=家電
Category: Id=3, Name=食品

コレクションの中身を確認するときに、各要素の内容が分かりやすくなります。

5. ToString overrideで注意すべきポイント

5-1. パスワードや個人情報など機密情報を出力しない

ToStringは、ログ出力やデバッグなどで意図せず呼び出されることがあります。そのため、機密情報を含めないことが非常に重要です。

避けるべき情報の例は次のとおりです。

C#
public class LoginUser
{
public string UserName { get; set; } = "";
public string Password { get; set; } = "";

public override string ToString()
{
return $"LoginUser: UserName={UserName}";
}
}

この例では、Passwordを出力していません。

次のような実装は避けるべきです。

C#
public override string ToString()
{
return $"LoginUser: UserName={UserName}, Password={Password}";
}

パスワード、アクセストークン、APIキー、住所、電話番号、メールアドレスなど、扱うシステムによって機密性の高い情報は出力しないようにしましょう。

5-2. null参照で例外が起きないようにする

ToStringの中でプロパティにアクセスする場合、nullに注意が必要です。

C#
public class Profile
{
public string? DisplayName { get; set; }

public override string ToString()
{
return $"Profile: DisplayName={DisplayName ?? "(未設定)"}";
}
}

DisplayNamenullの場合でも、??演算子を使えば安全に表示できます。

次のように、nullの可能性があるプロパティに対して直接メソッドを呼ぶと、例外が発生する可能性があります。

C#
public override string ToString()
{
return DisplayName.ToUpper();
}

DisplayNamenullの場合、NullReferenceExceptionが発生します。

安全に書くなら次のようにします。

C#
public override string ToString()
{
return (DisplayName ?? "").ToUpper();
}

ToStringは多くの場面で呼ばれる可能性があるため、できるだけ例外が起きにくい実装にしましょう。

5-3. 重い処理や副作用のある処理を書かない

ToStringには、重い処理や副作用のある処理を書かないようにします。

避けるべき処理の例は次のとおりです。

C#
public override string ToString()
{
// 避けたい例: データベースアクセスや外部API呼び出し
return LoadDataFromDatabase();
}

ToStringは、ログ出力、デバッグ、文字列補間などで頻繁に呼ばれる可能性があります。そのたびにデータベースアクセスや外部API通信が発生すると、パフォーマンス低下や予期しない副作用につながります。

また、ToStringの中で状態を変更する処理も避けるべきです。

C#
public override string ToString()
{
Count++;
return $"Count={Count}";
}

このような実装にすると、文字列化するだけでオブジェクトの状態が変わってしまいます。

ToStringは、基本的に「現在の状態を読み取って文字列にするだけ」の処理にとどめましょう。

5-4. 外部仕様や画面表示専用の整形を詰め込みすぎない

ToStringは便利ですが、画面表示やCSV出力、JSON出力などの専用フォーマットをすべて詰め込む場所ではありません。

たとえば、画面表示専用の文言をToStringに直接書くと、用途が増えたときに扱いにくくなります。

C#
public override string ToString()
{
return $"商品名: {Name} / 価格: {Price}円 / 在庫数: {Stock}個";
}

人間が読むログやデバッグ用途であれば問題ありませんが、画面表示の仕様として使い回す場合は注意が必要です。

画面表示用には別のメソッドやViewModel、機械処理用には専用のシリアライズ処理を用意したほうが安全です。

C#
public string ToDisplayText()
{
return $"商品名: {Name} / 価格: {Price}円";
}

public override string ToString()
{
return $"Product: Name={Name}, Price={Price}, Stock={Stock}";
}

ToStringは汎用的な文字列表現として使い、特定用途のフォーマットは専用メソッドに分けると保守しやすくなります。

5-5. 出力内容を後から変更した場合の影響に注意する

ToStringの出力形式を後から変更すると、思わぬ影響が出ることがあります。

特に、ログ解析やテストコード、外部システム連携などでToStringの結果に依存している場合は注意が必要です。

たとえば、最初は次の形式だったとします。

Product: Name=キーボード, Price=4980

後から次のように変更した場合、ログを解析している処理が影響を受ける可能性があります。

Product(Name=キーボード, Price=4980, Stock=12)

本来、ToStringの結果を機械処理の入力として強く依存するのはおすすめできません。機械処理が必要な場合は、専用のメソッドやDTO、JSONシリアライズなどを使いましょう。

6. よくあるエラー・疑問と解決方法

6-1. overrideできない場合に確認すること

ToStringoverrideできない場合は、メソッドの定義が正しいか確認しましょう。

正しい書き方は次のとおりです。

C#
public override string ToString()
{
return "文字列";
}

よくある間違いは次のようなものです。

C#
public string ToString()
{
return "文字列";
}

この場合、overrideが付いていません。コンパイル自体は通る場合がありますが、意図したオーバーライドではなく、警告が出ることがあります。

また、戻り値や引数が違うとoverrideできません。

C#
public override int ToString()
{
return 1;
}

これは戻り値がstringではないためエラーになります。

C#
public override string ToString(string format)
{
return format;
}

これは引数があるため、object.ToString()とは別のメソッドになり、overrideできません。

6-2. staticを付けてはいけない理由

ToStringoverrideするときにstaticを付けることはできません。

次の書き方は誤りです。

C#
public static override string ToString()
{
return "文字列";
}

ToStringはインスタンスごとの状態を文字列にするためのメソッドです。つまり、オブジェクトごとに異なる値を返すことが前提です。

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

public override string ToString()
{
return $"Person: Name={Name}";
}
}

この例では、Nameはインスタンスごとに異なります。そのため、ToStringもインスタンスメソッドとして定義する必要があります。

staticメソッドはクラスに属するメソッドであり、特定のインスタンスの状態を直接表すものではありません。そのため、ToStringoverrideには使えません。

6-3. 戻り値をstring以外にできない理由

ToStringの戻り値は必ずstringです。

次のように、戻り値をintにすることはできません。

C#
public override int ToString()
{
return 123;
}

overrideは、親クラスのメソッドと同じシグネチャで定義する必要があります。object.ToString()の戻り値はstringなので、オーバーライドする側もstringでなければなりません。

数値を返したい場合でも、文字列に変換して返します。

C#
public override string ToString()
{
return Id.ToString();
}

または、文字列補間を使って次のように書けます。

C#
public override string ToString()
{
return $"Id={Id}";
}

6-4. ToStringとConvert.ToStringの違い

ToStringConvert.ToStringは、どちらも値を文字列に変換するときに使われますが、nullの扱いに違いがあります。

C#
object? value = null;

// value.ToString(); はNullReferenceExceptionになる
string text = Convert.ToString(value);

valuenullの場合、value.ToString()を呼ぶとNullReferenceExceptionが発生します。一方、Convert.ToString(value)nullを安全に扱えます。

ただし、自作クラスの文字列表現を定義するという意味では、基本になるのはToStringoverrideです。

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

public override string ToString()
{
return $"User: Name={Name}";
}
}

このようにToStringoverrideしておけば、Convert.ToString(user)を使った場合にも、そのオブジェクトのToString結果が利用されます。

6-5. ToStringが自動で呼ばれるタイミング

ToStringは、文字列が必要な場面で自動的に呼ばれることがあります。

たとえば、文字列補間では次のように呼ばれます。

C#
var user = new User { Name = "田中" };

string message = $"ユーザー情報: {user}";

この場合、user.ToString()の結果が文字列に埋め込まれます。

文字列連結でも同様です。

C#
string message = "ユーザー情報: " + user;

Console.WriteLineでも、オブジェクトを渡すとToStringの結果が表示されます。

C#
Console.WriteLine(user);

そのため、ToStringは思ったより多くの場面で使われます。出力内容には注意し、機密情報や重い処理を含めないようにしましょう。

7. record・struct・継承クラスでのToString override

7-1. structでToStringをoverrideする方法

structでもToStringoverrideできます。

C#
public struct Point
{
public int X { get; set; }
public int Y { get; set; }

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

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

C#
var point = new Point { X = 10, Y = 20 };

Console.WriteLine(point);

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

Point: X=10, Y=20

structは値型ですが、objectから継承したToStringoverrideできます。座標、範囲、サイズなど、値の意味を分かりやすく表現したい場合に便利です。

7-2. record型のToStringとカスタマイズ方法

C#のrecord型は、標準で分かりやすいToStringが生成されます。

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

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

C#
var person = new PersonRecord("田中", 30);

Console.WriteLine(person);

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

PersonRecord { Name = 田中, Age = 30 }

通常のクラスと違い、recordではプロパティ名と値を含む文字列が自動的に出力されます。

カスタマイズしたい場合は、通常のクラスと同じようにToStringoverrideできます。

C#
public record PersonRecord(string Name, int Age)
{
public override string ToString()
{
return $"PersonRecord: {Name}さん({Age}歳)";
}
}

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

PersonRecord: 田中さん(30歳)

recordは標準のToStringが便利ですが、ログ形式や表示形式を統一したい場合はカスタマイズを検討するとよいでしょう。

7-3. 継承先でToStringを再overrideする場合

継承関係のあるクラスでは、親クラスでToStringoverrideし、さらに子クラスで再度overrideできます。

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

public override string ToString()
{
return $"Animal: Name={Name}";
}
}

子クラスでさらにoverrideします。

C#
public class Dog : Animal
{
public string Breed { get; set; } = "";

public override string ToString()
{
return $"Dog: Name={Name}, Breed={Breed}";
}
}

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

C#
var dog = new Dog
{
Name = "ポチ",
Breed = "柴犬"
};

Console.WriteLine(dog);

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

Dog: Name=ポチ, Breed=柴犬

子クラス固有のプロパティを含めたい場合は、継承先で再度ToStringoverrideすると分かりやすくなります。

7-4. base.ToStringを使うべきケース

子クラスのToStringで、親クラスの出力内容も活用したい場合はbase.ToString()を使えます。

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

public override string ToString()
{
return $"Name={Name}";
}
}

子クラス側で親クラスの文字列表現を使います。

C#
public class Dog : Animal
{
public string Breed { get; set; } = "";

public override string ToString()
{
return $"Dog: {base.ToString()}, Breed={Breed}";
}
}

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

Dog: Name=ポチ, Breed=柴犬

共通のプロパティは親クラスのToStringにまとめ、子クラスでは追加情報だけを加えると、重複を減らせます。

ただし、base.ToString()の出力形式を変更すると子クラスの出力にも影響します。親子の出力形式をどのように管理するかは、設計時に考えておくとよいでしょう。

8. ToString overrideのベストプラクティス

8-1. クラス名と主要プロパティを含める

ToStringには、クラス名と主要なプロパティを含めるのがおすすめです。

C#
public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}

クラス名があると、ログやデバッグ出力を見たときに、どの型のオブジェクトなのか分かりやすくなります。

複数のクラスが同じようなプロパティを持っている場合でも、クラス名があれば区別しやすくなります。

User: Id=1, Name=田中
Customer: Id=1, Name=田中

どちらもIdNameを持っていますが、クラス名があることで意味が明確になります。

8-2. ログやデバッグで読みやすい形式にする

ToStringの出力は、人間が読むことを意識して整えると便利です。

C#
public override string ToString()
{
return $"Order: Id={Id}, Status={Status}, Total={TotalAmount:N0}";
}

プロパティ名と値をセットで出すと、何の値なのかが分かりやすくなります。

Order: Id=1001, Status=Completed, Total=12,500

値だけを並べる形式は短く書けますが、意味が分かりにくくなることがあります。

1001, Completed, 12500

ログやデバッグで使うなら、多少長くなってもプロパティ名を含めるほうが安全です。

8-3. 機械処理用の文字列には専用メソッドを用意する

ToStringの結果を、CSV出力や外部連携などの機械処理に使うのは慎重に考えるべきです。

ToStringは、主に人間が読むための文字列表現として使われることが多いからです。

機械処理用の文字列が必要な場合は、専用メソッドを用意します。

C#
public class Product
{
public string Name { get; set; } = "";
public decimal Price { get; set; }

public string ToCsv()
{
return $"{Name},{Price}";
}

public override string ToString()
{
return $"Product: Name={Name}, Price={Price}";
}
}

このように分けておけば、ToStringの出力形式を変更しても、CSV出力への影響を抑えられます。

JSONが必要な場合も、ToStringに直接JSON文字列を書くのではなく、シリアライズ用の処理を使うほうが一般的です。

8-4. テストコードで出力結果を確認する

ToStringの出力が重要な意味を持つ場合は、テストコードで確認しておくと安心です。

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

public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}
}

テスト例は次のようになります。

C#
[Fact]
public void ToString_ReturnsExpectedText()
{
var user = new User
{
Id = 1,
Name = "田中"
};

Assert.Equal("User: Id=1, Name=田中", user.ToString());
}

ログやデバッグ用途だけであれば必須ではありませんが、出力形式を一定に保ちたい場合はテストが役立ちます。

8-5. チーム内で出力ルールを統一する

プロジェクト内でToStringの形式がバラバラだと、ログやデバッグ情報が読みにくくなります。

たとえば、あるクラスでは次の形式を使っているとします。

User: Id=1, Name=田中

別のクラスでは次のような形式になっていると、統一感がありません。

[Product] 10 / Keyboard / 4980

チーム内で、次のようなルールを決めておくと読みやすくなります。

ClassName: Property1=Value1, Property2=Value2

この形式で統一すると、ログ検索や目視確認がしやすくなります。

C#
public override string ToString()
{
return $"Product: Id={Id}, Name={Name}, Price={Price}";
}

大規模な開発では、ToStringの出力ルールもコーディング規約の一部として決めておくとよいでしょう。

9. C#のToString overrideに関するFAQ

9-1. すべてのクラスでToStringをoverrideすべき?

すべてのクラスでToStringoverrideする必要はありません。

ToStringoverrideすると便利なのは、ログやデバッグでオブジェクトの中身を確認したいクラスです。

たとえば、エンティティ、値オブジェクト、設定情報、リクエスト情報などはToStringを用意すると便利な場合があります。

一方、内部処理専用で文字列化する必要がないクラスや、機密情報を多く持つクラスでは、無理にoverrideしなくても構いません。

重要なのは、ToStringを呼んだときに意味のある、安全な文字列を返せるかどうかです。

9-2. ToStringでJSON形式を返してもよい?

技術的には、ToStringでJSON形式の文字列を返すことは可能です。

C#
public override string ToString()
{
return $"{{ \"Id\": {Id}, \"Name\": \"{Name}\" }}";
}

ただし、一般的にはおすすめしません。

JSONとして正しくエスケープする必要があり、プロパティが増えると管理が難しくなるためです。また、ToStringは人間が読むための簡易的な文字列表現として使われることが多く、JSON出力とは役割が異なります。

JSONが必要な場合は、専用のシリアライズ処理を使うほうが安全です。

C#
string json = JsonSerializer.Serialize(user);

ToStringはログやデバッグ向け、JSONはデータ交換や保存向け、と役割を分けるのがおすすめです。

9-3. ToStringで例外を投げてもよい?

ToStringの中で例外が発生する実装は、できるだけ避けるべきです。

ToStringは、ログ出力、デバッグ、文字列補間、Console.WriteLineなど、さまざまな場面で呼ばれます。そこで例外が発生すると、本来の処理とは関係ない場所で問題が起きる可能性があります。

避けたい例は次のとおりです。

C#
public override string ToString()
{
if (Name == null)
{
throw new InvalidOperationException("Name is null.");
}

return Name;
}

安全にするなら、次のように書きます。

C#
public override string ToString()
{
return $"Name={Name ?? "(null)"}";
}

ToStringは、できるだけ失敗しにくい実装にしておくのが基本です。

9-4. ToStringの書式は自由に決めてよい?

ToStringの書式は基本的に自由に決められます。

ただし、読みやすさと保守性を考えると、一定のルールを持たせるのがおすすめです。

よく使われる形式は次のようなものです。

ClassName: Property1=Value1, Property2=Value2

C#のコードでは次のように書けます。

C#
public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}

書式を自由に決められるからといって、用途ごとの表示形式をすべてToStringに詰め込むのは避けましょう。

画面表示、CSV、JSON、外部連携など、用途が明確なものは専用の処理に分けると安全です。

9-5. デバッグ表示だけならDebuggerDisplayとどちらを使うべき?

デバッグ時の表示だけを改善したい場合は、DebuggerDisplay属性を使う選択肢もあります。

C#
using System.Diagnostics;

[DebuggerDisplay("User: Id={Id}, Name={Name}")]
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

DebuggerDisplayは、主にVisual Studioなどのデバッガー上での表示を分かりやすくするためのものです。

一方、ToStringはデバッグだけでなく、ログ出力、Console.WriteLine、文字列補間など、実行時のさまざまな場面で使われます。

デバッグ画面だけを見やすくしたいならDebuggerDisplay、実行時にも文字列表現を使いたいならToString overrideを使うとよいでしょう。

両方を使うことも可能です。

C#
using System.Diagnostics;

[DebuggerDisplay("{ToString()}")]
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";

public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}
}

このようにしておくと、通常の文字列表現とデバッグ表示をそろえやすくなります。

まとめ

C#のToStringoverrideすると、自作クラスや構造体を分かりやすい文字列として表現できます。

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

C#
public override string ToString()
{
return "表示したい文字列";
}

ToStringoverrideすることで、Console.WriteLine、文字列補間、ログ出力、デバッグなどでオブジェクトの内容を確認しやすくなります。

実装するときは、クラス名と主要プロパティを含めると読みやすくなります。

C#
public override string ToString()
{
return $"User: Id={Id}, Name={Name}";
}

一方で、パスワードや個人情報などの機密情報を出力しないこと、nullで例外が起きないようにすること、重い処理や副作用を含めないことが重要です。

また、ToStringは人間が読むための文字列表現として使い、JSONやCSVなどの機械処理用フォーマットは専用の処理に分けるのがおすすめです。

C#で自作クラスを扱う際は、ログやデバッグで何を確認したいかを考えながら、適切にToStringoverrideしましょう。