C# objectとは?基本の使い方・型変換・注意点を初心者向けに徹底解説
はじめに
C#を学び始めると、早い段階でobjectという型を目にすることがあります。
たとえば、次のようなコードです。
C#object value = "Hello";
object number = 123;
object flag = true;
string、int、boolのように異なる型の値を、すべてobject型の変数に代入できています。
これは、C#におけるobjectが「すべての型の基底となる型」だからです。C# objectを理解すると、型変換、ボックス化、キャスト、ToStringやEqualsなどの基本メソッドの仕組みも理解しやすくなります。
一方で、objectは便利な反面、使い方を間違えると型安全性が下がったり、実行時エラーの原因になったりします。特に初心者のうちは、「何でも入れられる便利な型」とだけ覚えるのではなく、「使うべき場面」と「避けるべき場面」をあわせて理解することが大切です。
この記事では、C#のobjectとは何か、基本の使い方、型変換、ボックス化・アンボックス化、よくあるエラー、実践的なコード例まで初心者向けにわかりやすく解説します。
1. C#のobjectとは?初心者が最初に理解すべき基本
C#のobjectは、すべての型の親にあたる特別な型です。
C#では、文字列、数値、真偽値、配列、クラスのインスタンスなど、さまざまな型を扱います。これらの型はすべて、最終的にはobjectを基底として持っています。
そのため、object型の変数には、さまざまな型の値を代入できます。
C#object a = "C#";
object b = 100;
object c = true;
このように、異なる型の値を同じobject型として扱える点が大きな特徴です。
1-1. objectはすべての型の基底クラス
C#では、すべての型はSystem.Objectから派生しています。
たとえば、次のような型があります。
C#string text = "Hello";
int number = 10;
bool isActive = true;
string、int、boolはそれぞれ異なる型ですが、すべてobjectとして扱うことができます。
C#object obj1 = text;
object obj2 = number;
object obj3 = isActive;
これは、C#の型システムにおいて、すべての型が共通の親としてobjectを持っているためです。
クラスを自分で作った場合も同じです。
C#class User
{
public string Name { get; set; }
}
User user = new User { Name = "Taro" };
object obj = user;
Userクラスは明示的に何かを継承していなくても、暗黙的にobjectを継承しています。
つまり、次のようなイメージです。
C#class User : object
{
}
実際にはこのように書く必要はありませんが、考え方としては「すべてのクラスはobjectを継承している」と理解するとわかりやすいです。
1-2. System.Objectとobjectキーワードの関係
C#にはobjectというキーワードがありますが、これはSystem.Objectの別名です。
つまり、次の2つは同じ意味です。
C#object value1 = "Hello";
System.Object value2 = "Hello";
一般的なC#のコードでは、System.Objectよりもobjectを使うことが多いです。
C#object value = 123;
objectはC#の組み込みキーワードであり、読みやすく短く書けるため、通常はこちらを使えば問題ありません。
同じような関係には、次のようなものがあります。
C#int // System.Int32
string // System.String
bool // System.Boolean
object // System.Object
C#では、基本的な型に対してキーワードが用意されています。objectもその一つです。
1-3. 値型・参照型を問わず扱える理由
C#の型は大きく分けると、値型と参照型があります。
値型の例は次のとおりです。
C#int number = 10;
double price = 99.9;
bool isActive = true;
参照型の例は次のとおりです。
C#string text = "Hello";
int[] numbers = { 1, 2, 3 };
User user = new User();
objectは、値型も参照型も扱えます。
C#object a = 10; // intは値型
object b = "Hello"; // stringは参照型
object c = true; // boolは値型
object d = new User(); // Userは参照型
参照型の場合は、もともとオブジェクトとして扱われるため、そのままobjectに代入できます。
値型の場合は、objectに代入するときに「ボックス化」という処理が行われます。
C#int number = 10;
object obj = number; // ボックス化
ボックス化については後ほど詳しく解説しますが、まずは「値型も参照型もobjectに代入できる」と覚えておけば大丈夫です。
1-4. object型が使われる主な場面
object型は、主に次のような場面で使われます。
まず、さまざまな型の値をまとめて扱いたい場合です。
C#object[] values = { "Apple", 100, true };
この配列には、string、int、boolが混在しています。
また、どの型が渡されるかわからないメソッドを作る場合にも使われます。
C#void PrintValue(object value)
{
Console.WriteLine(value);
}
このメソッドには、文字列でも数値でも真偽値でも渡せます。
C#PrintValue("Hello");
PrintValue(123);
PrintValue(true);
ただし、実務では「何でも受け取れるから便利」という理由だけでobjectを多用するのはおすすめできません。
型が曖昧になるため、あとからキャストが必要になったり、実行時エラーの原因になったりするからです。
2. C# objectの基本的な使い方
ここからは、C# objectの基本的な使い方をコードで確認していきます。
object型の宣言方法、値の代入方法、配列やクラスの扱い方、Console.WriteLineでの表示方法を順番に見ていきましょう。
2-1. object型の変数を宣言する方法
object型の変数は、次のように宣言します。
C#object value;
値を同時に代入することもできます。
C#object value = "Hello";
あとから別の型の値を代入することも可能です。
C#object value = "Hello";
value = 100;
value = true;
このように、同じ変数に異なる型の値を代入できます。
ただし、これは便利である一方、現在その変数に何の型の値が入っているのかがわかりにくくなる原因にもなります。
C#object value = "Hello";
value = 100;
// valueは今stringではなくint
コードが長くなると、objectの中身を追いかけるのが難しくなるため注意が必要です。
2-2. 文字列・数値・boolをobjectに代入する例
objectには、文字列、数値、真偽値などを代入できます。
C#object text = "C# object";
object number = 123;
object isActive = true;
Console.WriteLine(text);
Console.WriteLine(number);
Console.WriteLine(isActive);
実行結果は次のようになります。
C#C# object
123
True
Console.WriteLineにobjectを渡すと、内部的には値のToStringメソッドが使われて文字列として表示されます。
数値や真偽値も問題なく表示できます。
C#object price = 1200;
object result = false;
Console.WriteLine(price);
Console.WriteLine(result);
出力結果は次のとおりです。
C#1200
False
2-3. 配列やクラスのインスタンスをobjectで扱う例
objectには、配列も代入できます。
C#int[] numbers = { 1, 2, 3 };
object obj = numbers;
ただし、object型のままでは配列の要素に直接アクセスできません。
C#object obj = new int[] { 1, 2, 3 };
// これはエラー
// Console.WriteLine(obj[0]);
要素にアクセスしたい場合は、元の型にキャストする必要があります。
C#object obj = new int[] { 1, 2, 3 };
int[] numbers = (int[])obj;
Console.WriteLine(numbers[0]);
出力結果は次のとおりです。
C#1
クラスのインスタンスもobjectで扱えます。
C#class User
{
public string Name { get; set; }
}
User user = new User { Name = "Yamada" };
object obj = user;
この場合も、object型のままではNameプロパティにはアクセスできません。
C#// これはエラー
// Console.WriteLine(obj.Name);
元のUser型に戻す必要があります。
C#User user2 = (User)obj;
Console.WriteLine(user2.Name);
出力結果は次のとおりです。
C#Yamada
2-4. object型の値をConsole.WriteLineで表示する例
object型の値は、Console.WriteLineで簡単に表示できます。
C#object value1 = "Hello";
object value2 = 100;
object value3 = true;
Console.WriteLine(value1);
Console.WriteLine(value2);
Console.WriteLine(value3);
出力結果は次のようになります。
C#Hello
100
True
独自クラスのインスタンスを表示するとどうなるでしょうか。
C#class User
{
public string Name { get; set; }
}
User user = new User { Name = "Sato" };
object obj = user;
Console.WriteLine(obj);
この場合、出力結果はクラス名のようになります。
C#User
よりわかりやすく表示したい場合は、ToStringメソッドをオーバーライドします。
C#class User
{
public string Name { get; set; }
public override string ToString()
{
return $"User Name: {Name}";
}
}
この状態で再度表示すると、次のようになります。
C#User user = new User { Name = "Sato" };
object obj = user;
Console.WriteLine(obj);
出力結果は次のとおりです。
C#User Name: Sato
3. object型と型変換の仕組み
objectを使ううえで特に重要なのが型変換です。
objectにはさまざまな型を代入できますが、objectから元の型として使うには、基本的に型変換が必要です。
ここでは、暗黙的な型変換、明示的なキャスト、is演算子、as演算子、パターンマッチングについて解説します。
3-1. objectに代入するときの暗黙的な型変換
ある型の値をobjectに代入するときは、基本的に明示的なキャストは不要です。
C#string text = "Hello";
object obj = text;
これは暗黙的に変換されます。
数値の場合も同じです。
C#int number = 10;
object obj = number;
値型であるintをobjectに代入する場合は、内部的にボックス化が行われます。
C#int number = 10;
object obj = number; // 暗黙的な変換 + ボックス化
このように、objectへの代入は簡単にできます。
しかし、逆方向、つまりobjectから具体的な型に戻すときは注意が必要です。
3-2. objectから元の型に戻す明示的なキャスト
object型の値を元の型として使うには、明示的なキャストが必要です。
C#object obj = "Hello";
string text = (string)obj;
Console.WriteLine(text);
出力結果は次のとおりです。
C#Hello
数値の場合も同じです。
C#object obj = 100;
int number = (int)obj;
Console.WriteLine(number);
出力結果は次のとおりです。
C#100
ただし、実際の型と違う型にキャストするとエラーになります。
C#object obj = 100;
// 実行時エラー
string text = (string)obj;
このコードでは、objの中身はintです。それをstringとして取り出そうとしているため、InvalidCastExceptionが発生します。
安全に扱うには、キャストする前に型を確認することが重要です。
3-3. is演算子で型を確認する方法
is演算子を使うと、objectの中身が特定の型かどうかを確認できます。
C#object obj = "Hello";
if (obj is string)
{
string text = (string)obj;
Console.WriteLine(text);
}
出力結果は次のとおりです。
C#Hello
intかどうかを確認する場合は、次のように書きます。
C#object obj = 123;
if (obj is int)
{
int number = (int)obj;
Console.WriteLine(number);
}
isを使えば、間違った型にキャストしてエラーになるリスクを減らせます。
ただし、最近のC#では、次のようにパターンマッチングを使う書き方がよく使われます。
C#object obj = "Hello";
if (obj is string text)
{
Console.WriteLine(text);
}
この書き方では、型チェックと変数への代入を同時に行えます。
3-4. as演算子で安全に変換する方法
as演算子を使うと、キャストに失敗した場合に例外を出さず、nullを返すことができます。
C#object obj = "Hello";
string text = obj as string;
if (text != null)
{
Console.WriteLine(text);
}
出力結果は次のとおりです。
C#Hello
もし変換できない場合は、nullになります。
C#object obj = 123;
string text = obj as string;
if (text == null)
{
Console.WriteLine("stringではありません");
}
出力結果は次のとおりです。
C#stringではありません
ただし、as演算子は主に参照型やnull許容型に対して使います。
たとえば、次のようにintへ直接asで変換することはできません。
C#object obj = 123;
// これはコンパイルエラー
// int number = obj as int;
値型に対して安全に変換したい場合は、isによるパターンマッチングを使うのがわかりやすいです。
C#object obj = 123;
if (obj is int number)
{
Console.WriteLine(number);
}
3-5. パターンマッチングを使った型判定と変換
C#では、パターンマッチングを使うと、型チェックと変換を簡潔に書けます。
C#object obj = "Hello";
if (obj is string text)
{
Console.WriteLine(text.ToUpper());
}
出力結果は次のとおりです。
C#HELLO
複数の型に応じて処理を分けたい場合は、switch式やswitch文を使うと便利です。
C#object value = 123;
switch (value)
{
case string text:
Console.WriteLine($"文字列: {text}");
break;
case int number:
Console.WriteLine($"整数: {number}");
break;
case bool flag:
Console.WriteLine($"真偽値: {flag}");
break;
default:
Console.WriteLine("その他の型です");
break;
}
出力結果は次のとおりです。
C#整数: 123
objectを使う場合、パターンマッチングは非常に便利です。型ごとの処理を安全に分岐でき、不要なキャストを減らせます。
4. object型で注意すべきボックス化・アンボックス化
objectを理解するうえで重要なのが、ボックス化とアンボックス化です。
特にint、double、boolなどの値型をobjectとして扱う場合、内部でボックス化が発生します。
この仕組みを知らないと、予期しないエラーやパフォーマンス低下につながることがあります。
4-1. ボックス化とは何か
ボックス化とは、値型の値をobject型として扱うために、ヒープ領域上のオブジェクトに包み込む処理のことです。
たとえば、次のコードを見てください。
C#int number = 10;
object obj = number;
intは値型です。一方、objectは参照型です。
そのため、intの値をobjectとして扱うには、値をオブジェクトとして包む必要があります。この処理がボックス化です。
イメージとしては、次のような流れです。
C#int number = 10; // 値型
object obj = number; // 10を箱に入れてobjectとして扱う
「値を箱に入れる」ので、ボックス化と呼ばれます。
4-2. アンボックス化とは何か
アンボックス化とは、ボックス化された値を元の値型として取り出す処理です。
C#int number = 10;
object obj = number; // ボックス化
int result = (int)obj; // アンボックス化
objectの中に入っているintの値を、再びintとして取り出しています。
アンボックス化では、正しい型にキャストする必要があります。
C#object obj = 10;
int number = (int)obj; // OK
しかし、違う型で取り出そうとするとエラーになります。
C#object obj = 10;
// 実行時エラー
double number = (double)obj;
objの中身はintなので、doubleとして直接アンボックス化することはできません。
この場合は、いったんintとして取り出してから変換します。
C#object obj = 10;
int number = (int)obj;
double result = number;
または、Convert.ToDoubleを使う方法もあります。
C#object obj = 10;
double result = Convert.ToDouble(obj);
Console.WriteLine(result);
4-3. 値型をobjectに代入するときに起きること
値型をobjectに代入すると、ボックス化が起きます。
C#int number = 100;
object obj = number;
このとき、numberの値がコピーされて、objectとして別の場所に保持されます。
そのため、元の変数を変更しても、objectに入れた値は変わりません。
C#int number = 100;
object obj = number;
number = 200;
Console.WriteLine(number);
Console.WriteLine(obj);
出力結果は次のとおりです。
C#200
100
objには、ボックス化された時点の値である100が入っています。
このように、値型をobjectに代入すると、値がコピーされる点に注意しましょう。
4-4. InvalidCastExceptionが発生する原因
objectを使っていると、InvalidCastExceptionが発生することがあります。
よくある原因は、実際の型と違う型にキャストしていることです。
C#object obj = 123;
// objの中身はintなので、stringにはキャストできない
string text = (string)obj;
このコードは実行時にエラーになります。
また、数値型同士でも注意が必要です。
C#object obj = 123;
// objの中身はintなので、doubleとして直接取り出せない
double number = (double)obj;
intからdoubleへの変換は通常できますが、objectに入っている値をアンボックス化する場合は、まず正確な型で取り出す必要があります。
正しい書き方は次のようになります。
C#object obj = 123;
int number = (int)obj;
double result = number;
または、変換処理を使います。
C#object obj = 123;
double result = Convert.ToDouble(obj);
キャストに失敗しないためには、is演算子やパターンマッチングを使って型を確認するのが安全です。
C#object obj = 123;
if (obj is int number)
{
Console.WriteLine(number);
}
4-5. パフォーマンス面で注意すべきポイント
objectは便利ですが、値型を扱うときにはボックス化とアンボックス化のコストが発生します。
たとえば、大量の数値をobject配列で扱うと、各値がボックス化されます。
C#object[] values = new object[100000];
for (int i = 0; i < values.Length; i++)
{
values[i] = i; // intがobjectにボックス化される
}
その後、取り出すときにはアンボックス化が必要です。
C#int total = 0;
foreach (object value in values)
{
total += (int)value; // アンボックス化
}
小さなプログラムでは大きな問題にならないことも多いですが、大量データを扱う処理やパフォーマンスが重要な場面では注意が必要です。
このような場合は、objectではなくジェネリクスを使う方が適しています。
C#List<int> numbers = new List<int>();
for (int i = 0; i < 100000; i++)
{
numbers.Add(i);
}
List<int>であれば、intをそのまま扱えるため、不要なボックス化を避けられます。
5. object型でよく使うメソッド
objectはすべての型の基底クラスなので、すべての型はobjectが持つ基本メソッドを利用できます。
代表的なメソッドは次の4つです。
C#ToString()
Equals()
GetHashCode()
GetType()
これらはC#の基本的な処理でよく使われます。
5-1. ToStringメソッドで文字列に変換する
ToStringメソッドは、オブジェクトを文字列として表現するためのメソッドです。
C#object value = 123;
string text = value.ToString();
Console.WriteLine(text);
出力結果は次のとおりです。
C#123
文字列の場合も使えます。
C#object value = "Hello";
Console.WriteLine(value.ToString());
出力結果は次のとおりです。
C#Hello
独自クラスでは、ToStringをオーバーライドすることで表示内容を変更できます。
C#class Product
{
public string Name { get; set; }
public int Price { get; set; }
public override string ToString()
{
return $"{Name}: {Price}円";
}
}
使い方は次のとおりです。
C#object product = new Product
{
Name = "Pen",
Price = 120
};
Console.WriteLine(product.ToString());
出力結果は次のようになります。
C#Pen: 120円
Console.WriteLine(product)と書いた場合も、内部的にはToStringが呼ばれます。
5-2. Equalsメソッドで値を比較する
Equalsメソッドは、2つのオブジェクトが等しいかどうかを比較するために使います。
C#object a = 10;
object b = 10;
Console.WriteLine(a.Equals(b));
出力結果は次のとおりです。
C#True
文字列でも比較できます。
C#object a = "Hello";
object b = "Hello";
Console.WriteLine(a.Equals(b));
出力結果は次のとおりです。
C#True
ただし、クラスのインスタンスを比較する場合は注意が必要です。
C#class User
{
public string Name { get; set; }
}
object user1 = new User { Name = "Taro" };
object user2 = new User { Name = "Taro" };
Console.WriteLine(user1.Equals(user2));
この場合、出力結果は通常次のようになります。
C#False
Nameが同じでも、別々のインスタンスなので等しくないと判定されます。
値の内容で比較したい場合は、Equalsをオーバーライドする必要があります。
C#class User
{
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj is User other)
{
return Name == other.Name;
}
return false;
}
public override int GetHashCode()
{
return Name?.GetHashCode() ?? 0;
}
}
このようにすると、Nameが同じ場合に等しいと判定できます。
5-3. GetHashCodeメソッドの役割
GetHashCodeメソッドは、オブジェクトのハッシュ値を取得するためのメソッドです。
C#object value = "Hello";
Console.WriteLine(value.GetHashCode());
ハッシュ値は、辞書やハッシュセットなどで値を効率よく管理するために使われます。
たとえば、DictionaryやHashSetでは、値の検索や重複判定のためにハッシュコードが利用されます。
C#HashSet<string> names = new HashSet<string>();
names.Add("Taro");
names.Add("Taro");
Console.WriteLine(names.Count);
出力結果は次のとおりです。
C#1
独自クラスでEqualsをオーバーライドする場合は、基本的にGetHashCodeもあわせてオーバーライドします。
C#public override int GetHashCode()
{
return Name?.GetHashCode() ?? 0;
}
Equalsで等しいと判定されるオブジェクトは、同じハッシュコードを返すように設計する必要があります。
5-4. GetTypeメソッドで実際の型を取得する
GetTypeメソッドを使うと、objectに入っている実際の型を取得できます。
C#object value = "Hello";
Console.WriteLine(value.GetType());
出力結果は次のようになります。
C#System.String
数値の場合は次のようになります。
C#object value = 123;
Console.WriteLine(value.GetType());
出力結果は次のとおりです。
C#System.Int32
GetTypeは、デバッグやログ出力でよく使われます。
C#void PrintType(object value)
{
Console.WriteLine($"値: {value}");
Console.WriteLine($"型: {value.GetType()}");
}
PrintType("Hello");
PrintType(100);
PrintType(true);
出力結果は次のようになります。
C#値: Hello
型: System.String
値: 100
型: System.Int32
値: True
型: System.Boolean
ただし、valueがnullの場合にGetTypeを呼び出すと、NullReferenceExceptionが発生します。
C#object value = null;
// 実行時エラー
// Console.WriteLine(value.GetType());
安全に書くなら、先にnullチェックを行います。
C#object value = null;
if (value != null)
{
Console.WriteLine(value.GetType());
}
else
{
Console.WriteLine("nullです");
}
6. object型と他の型の違い
objectを理解するには、var、dynamic、string、ジェネリクス、インターフェースとの違いも重要です。
特に初心者は、objectとvarを混同しやすいため注意しましょう。
6-1. objectとvarの違い
objectとvarはまったく別のものです。
objectは型そのものです。
C#object value = "Hello";
この場合、変数valueの型はobjectです。
一方、varはコンパイラに型を推論してもらうためのキーワードです。
C#var value = "Hello";
この場合、変数valueの型はstringになります。
つまり、次の2つは意味が違います。
C#object a = "Hello"; // aの型はobject
var b = "Hello"; // bの型はstring
違いは、次のコードを見るとわかりやすいです。
C#object a = "Hello";
var b = "Hello";
a = 123; // OK
// b = 123; // コンパイルエラー
aはobject型なので、あとからintを代入できます。
しかし、bは最初の代入によってstring型と推論されているため、intは代入できません。
varは「何でも入れられる型」ではありません。あくまで、コンパイラが型を決めてくれるだけです。
6-2. objectとdynamicの違い
objectとdynamicは、どちらもさまざまな型の値を代入できます。
C#object obj = "Hello";
dynamic dyn = "Hello";
違いは、メソッドやプロパティを呼び出すときにあります。
objectの場合、コンパイル時にはobject型として扱われます。
C#object obj = "Hello";
// コンパイルエラー
// Console.WriteLine(obj.Length);
objの中身が文字列でも、変数の型はobjectなので、Lengthプロパティには直接アクセスできません。
使うにはキャストが必要です。
C#object obj = "Hello";
string text = (string)obj;
Console.WriteLine(text.Length);
一方、dynamicではコンパイル時の型チェックが緩くなり、実行時にメンバーが解決されます。
C#dynamic dyn = "Hello";
Console.WriteLine(dyn.Length);
出力結果は次のとおりです。
C#5
ただし、存在しないメンバーを呼び出すと実行時エラーになります。
C#dynamic dyn = "Hello";
// 実行時エラー
// Console.WriteLine(dyn.NotFoundMethod());
dynamicは便利ですが、型安全性が下がるため、通常の処理ではあまり多用しません。
基本的には、型が明確な場合は具体的な型を使い、どうしても実行時に柔軟な呼び出しが必要な場合にdynamicを検討します。
6-3. objectとstringの違い
stringは文字列専用の型です。
C#string text = "Hello";
一方、objectはさまざまな型を扱える型です。
C#object value = "Hello";
value = 123;
value = true;
objectに文字列を入れることはできますが、objectは文字列専用ではありません。
C#object value = "Hello";
// 直接Lengthは使えない
// Console.WriteLine(value.Length);
文字列として扱いたい場合は、キャストが必要です。
C#object value = "Hello";
string text = (string)value;
Console.WriteLine(text.Length);
出力結果は次のとおりです。
C#5
最初から文字列だけを扱うことがわかっている場合は、objectではなくstringを使うべきです。
C#string name = "Taro";
objectを使うと、文字列以外も入れられてしまうため、コードの意図が曖昧になります。
6-4. objectとジェネリクスの違い
ジェネリクスは、型安全性を保ちながら汎用的な処理を作るための仕組みです。
たとえば、objectを使ったメソッドは次のように書けます。
C#void PrintValue(object value)
{
Console.WriteLine(value);
}
このメソッドは何でも受け取れます。
C#PrintValue("Hello");
PrintValue(123);
PrintValue(true);
一方、ジェネリクスを使うと、型を保ったまま汎用的に処理できます。
C#void PrintValue<T>(T value)
{
Console.WriteLine(value);
}
この場合も、さまざまな型を扱えます。
C#PrintValue<string>("Hello");
PrintValue<int>(123);
PrintValue<bool>(true);
型引数は省略できることも多いです。
C#PrintValue("Hello");
PrintValue(123);
PrintValue(true);
ジェネリクスのメリットは、型情報が失われにくいことです。
たとえば、objectを使ったリストでは、取り出すときにキャストが必要です。
C#List<object> values = new List<object>();
values.Add("Hello");
string text = (string)values[0];
一方、List<string>ならキャスト不要です。
C#List<string> values = new List<string>();
values.Add("Hello");
string text = values[0];
特定の型を扱うコレクションやメソッドでは、objectよりジェネリクスを使う方が安全で読みやすいです。
6-5. objectとインターフェースの使い分け
インターフェースは、共通の機能や振る舞いを表すために使います。
たとえば、次のようなインターフェースを考えます。
C#interface IPrintable
{
void Print();
}
このインターフェースを実装したクラスを作ります。
C#class Report : IPrintable
{
public void Print()
{
Console.WriteLine("レポートを印刷します");
}
}
class Invoice : IPrintable
{
public void Print()
{
Console.WriteLine("請求書を印刷します");
}
}
この場合、objectではなくIPrintableを使うことで、「印刷できるもの」を表現できます。
C#void PrintItem(IPrintable item)
{
item.Print();
}
objectで書くと、何が渡されるかわかりません。
C#void PrintItem(object item)
{
// item.Print() は直接呼べない
}
共通の操作がある場合は、objectではなくインターフェースを使う方が適切です。
objectは「何でも受け取る」ための型ですが、インターフェースは「特定の機能を持つものを受け取る」ための型です。
7. C# objectを使うメリット・デメリット
C# objectには、便利な面と注意すべき面があります。
「何でも入れられる」という特徴は大きなメリットですが、同時に型安全性が下がるというデメリットにもなります。
7-1. どんな型でも受け取れる柔軟性
objectの最大のメリットは、さまざまな型を受け取れることです。
C#void Show(object value)
{
Console.WriteLine(value);
}
Show("Hello");
Show(100);
Show(true);
このように、文字列、数値、真偽値などを同じメソッドで処理できます。
異なる型の値を一時的にまとめて扱いたい場合にも便利です。
C#object[] values = { "Apple", 100, true };
foreach (object value in values)
{
Console.WriteLine(value);
}
出力結果は次のとおりです。
C#Apple
100
True
型が固定できない場面では、objectの柔軟性が役立ちます。
7-2. 共通処理を作りやすいメリット
objectを使うと、型に依存しない共通処理を作りやすくなります。
たとえば、ログ出力のような処理です。
C#void Log(object value)
{
Console.WriteLine($"LOG: {value}");
}
このメソッドには、さまざまな値を渡せます。
C#Log("処理開始");
Log(200);
Log(DateTime.Now);
Console.WriteLineのように、渡された値を文字列として表示するだけであれば、objectでも十分な場合があります。
また、古いAPIや一部のフレームワークでは、汎用的な値を受け取るためにobjectが使われていることもあります。
7-3. 型安全性が下がるデメリット
objectの大きなデメリットは、型安全性が下がることです。
たとえば、次のコードを見てください。
C#object value = "Hello";
int number = (int)value;
このコードはコンパイルは通る可能性がありますが、実行時にエラーになります。
valueの中身はstringなのに、intとして取り出そうとしているからです。
具体的な型を使っていれば、このようなミスはコンパイル時に検出できます。
C#string value = "Hello";
// コンパイルエラー
// int number = value;
objectを使うと、間違いが実行時まで発見されないことがあります。
これは、保守性や安全性の面で大きなデメリットです。
7-4. キャストが増えてコードが読みにくくなる問題
objectから具体的な型として値を使うには、キャストが必要です。
C#object value = "Hello";
string text = (string)value;
Console.WriteLine(text.Length);
このようなキャストが増えると、コードが読みにくくなります。
C#object value1 = "Apple";
object value2 = 100;
string name = (string)value1;
int price = (int)value2;
Console.WriteLine($"{name}: {price}円");
最初から型がわかっているなら、具体的な型を使った方が明確です。
C#string name = "Apple";
int price = 100;
Console.WriteLine($"{name}: {price}円");
この方が、何の値を扱っているのかがすぐにわかります。
7-5. 実務ではobjectを多用しない方がよい理由
実務では、objectを多用しない方がよいケースが多いです。
理由は、型が曖昧になり、バグを発見しにくくなるからです。
C#void Process(object value)
{
int number = (int)value;
Console.WriteLine(number * 2);
}
このメソッドは、intが渡されることを前提にしています。
しかし、引数の型がobjectなので、文字列や真偽値も渡せてしまいます。
C#Process("Hello"); // 実行時エラー
最初からintを使えば、間違った値はコンパイル時に防げます。
C#void Process(int value)
{
Console.WriteLine(value * 2);
}
型が決まっているなら、具体的な型を使うべきです。
複数の型に対応したい場合でも、まずはジェネリクスやインターフェースで表現できないかを考えるのが実務的です。
8. object型でよくあるエラーと対処法
objectを使うときによくあるエラーには、キャスト失敗、null参照、想定外の型の受け取りなどがあります。
これらは、型チェックやnullチェックを適切に行うことで防げます。
8-1. キャスト失敗によるInvalidCastException
最もよくあるエラーが、キャスト失敗によるInvalidCastExceptionです。
C#object value = "Hello";
int number = (int)value;
このコードでは、valueの中身はstringです。それをintとして取り出そうとしているため、実行時エラーになります。
対処法は、キャスト前に型を確認することです。
C#object value = "Hello";
if (value is int number)
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("intではありません");
}
出力結果は次のとおりです。
C#intではありません
objectをキャストするときは、「本当にその型が入っているか」を確認する習慣をつけましょう。
8-2. null参照によるNullReferenceException
objectにはnullを代入できます。
C#object value = null;
しかし、nullに対してメソッドを呼び出すとNullReferenceExceptionが発生します。
C#object value = null;
// 実行時エラー
// Console.WriteLine(value.ToString());
対処法は、nullチェックを行うことです。
C#object value = null;
if (value != null)
{
Console.WriteLine(value.ToString());
}
else
{
Console.WriteLine("値はnullです");
}
また、null条件演算子を使う方法もあります。
C#object value = null;
Console.WriteLine(value?.ToString() ?? "値はnullです");
このコードでは、valueがnullの場合に"値はnullです"が表示されます。
8-3. 想定外の型が渡されたときの対処
objectを引数にすると、想定外の型が渡される可能性があります。
C#void PrintLength(object value)
{
string text = (string)value;
Console.WriteLine(text.Length);
}
このメソッドは文字列を前提にしていますが、引数がobjectなので数値も渡せてしまいます。
C#PrintLength(123); // 実行時エラー
安全にするには、型チェックを行います。
C#void PrintLength(object value)
{
if (value is string text)
{
Console.WriteLine(text.Length);
}
else
{
Console.WriteLine("文字列ではありません");
}
}
さらに、文字列だけを受け取るべきメソッドなら、最初からstring型にする方が適切です。
C#void PrintLength(string text)
{
Console.WriteLine(text.Length);
}
objectを使う前に、「本当に何でも受け取る必要があるのか」を考えることが大切です。
8-4. 型チェックを使って安全に処理する書き方
objectを安全に扱うには、パターンマッチングを使うのがおすすめです。
C#void PrintValue(object value)
{
if (value is string text)
{
Console.WriteLine($"文字列: {text}");
}
else if (value is int number)
{
Console.WriteLine($"整数: {number}");
}
else if (value is bool flag)
{
Console.WriteLine($"真偽値: {flag}");
}
else if (value == null)
{
Console.WriteLine("nullです");
}
else
{
Console.WriteLine($"その他の型: {value.GetType()}");
}
}
使い方は次のとおりです。
C#PrintValue("Hello");
PrintValue(123);
PrintValue(true);
PrintValue(null);
出力結果は次のようになります。
C#文字列: Hello
整数: 123
真偽値: True
nullです
switch文を使うと、さらに見通しよく書けます。
C#void PrintValue(object value)
{
switch (value)
{
case string text:
Console.WriteLine($"文字列: {text}");
break;
case int number:
Console.WriteLine($"整数: {number}");
break;
case bool flag:
Console.WriteLine($"真偽値: {flag}");
break;
case null:
Console.WriteLine("nullです");
break;
default:
Console.WriteLine($"その他の型: {value.GetType()}");
break;
}
}
objectを使う場合は、このように型ごとの処理を明確に分けると安全です。
9. object型を使った実践的なコード例
ここでは、C# objectを使った実践的なコード例を紹介します。
複数の型を受け取る例、メソッドの引数にobjectを指定する例、object配列を使う例、型ごとに処理を分岐する例を見ていきましょう。
9-1. 複数の型をobjectで受け取るサンプル
次のメソッドは、さまざまな型の値を受け取って表示します。
C#void Show(object value)
{
Console.WriteLine(value);
}
呼び出し例です。
C#Show("C#");
Show(100);
Show(true);
Show(DateTime.Now);
このように、objectを使うと型に関係なく値を受け取れます。
ただし、単に表示するだけなら便利ですが、型ごとに特別な処理をしたい場合は型チェックが必要です。
C#void ShowDetail(object value)
{
if (value is string text)
{
Console.WriteLine($"文字列の長さ: {text.Length}");
}
else if (value is int number)
{
Console.WriteLine($"2倍の値: {number * 2}");
}
else
{
Console.WriteLine($"値: {value}");
}
}
使い方は次のとおりです。
C#ShowDetail("Hello");
ShowDetail(50);
ShowDetail(true);
出力結果は次のようになります。
C#文字列の長さ: 5
2倍の値: 100
値: True
9-2. メソッドの引数にobjectを指定する例
ログ出力のような処理では、objectを引数にすると便利な場合があります。
C#void Log(object message)
{
Console.WriteLine($"[LOG] {message}");
}
使い方は次のとおりです。
C#Log("処理を開始しました");
Log(404);
Log(DateTime.Now);
出力結果の例です。
C#[LOG] 処理を開始しました
[LOG] 404
[LOG] 2026/06/07 10:00:00
ただし、nullが渡される可能性もあります。
C#Log(null);
より安全に書くなら、次のようにします。
C#void Log(object message)
{
string text = message?.ToString() ?? "null";
Console.WriteLine($"[LOG] {text}");
}
これで、nullが渡されてもエラーになりません。
9-3. object配列で異なる型の値を管理する例
object配列を使うと、異なる型の値を1つの配列にまとめられます。
C#object[] values = new object[]
{
"Apple",
150,
true,
DateTime.Now
};
配列の中身を表示する例です。
C#foreach (object value in values)
{
Console.WriteLine(value);
}
出力結果の例です。
C#Apple
150
True
2026/06/07 10:00:00
型ごとに処理を変えることもできます。
C#foreach (object value in values)
{
switch (value)
{
case string text:
Console.WriteLine($"文字列: {text}");
break;
case int number:
Console.WriteLine($"数値: {number}");
break;
case bool flag:
Console.WriteLine($"真偽値: {flag}");
break;
case DateTime date:
Console.WriteLine($"日時: {date}");
break;
default:
Console.WriteLine($"その他: {value}");
break;
}
}
ただし、実務では異なる型の値を無理にobject[]で管理するより、専用のクラスを作った方がよい場合も多いです。
たとえば、商品名、価格、在庫有無を扱うなら、次のようなクラスにした方が明確です。
C#class Product
{
public string Name { get; set; }
public int Price { get; set; }
public bool InStock { get; set; }
}
9-4. 型ごとに処理を分岐する例
objectを使う場合、型ごとに処理を分岐する場面があります。
次の例では、渡された値の型によって処理を変えています。
C#void Process(object value)
{
switch (value)
{
case string text:
Console.WriteLine($"文字列です。長さ: {text.Length}");
break;
case int number:
Console.WriteLine($"整数です。2倍: {number * 2}");
break;
case double d:
Console.WriteLine($"小数です。半分: {d / 2}");
break;
case bool flag:
Console.WriteLine(flag ? "trueです" : "falseです");
break;
case null:
Console.WriteLine("nullです");
break;
default:
Console.WriteLine($"未対応の型です: {value.GetType()}");
break;
}
}
呼び出し例です。
C#Process("Hello");
Process(10);
Process(12.5);
Process(false);
Process(null);
出力結果は次のようになります。
C#文字列です。長さ: 5
整数です。2倍: 20
小数です。半分: 6.25
falseです
nullです
このように、パターンマッチングを使うと、objectの中身に応じた安全な処理を書けます。
10. C# objectを使うべき場面・避けるべき場面
objectはC#の基本的な型ですが、いつでも使えばよいわけではありません。
むしろ、実務ではobjectの使用を最小限にすることが多いです。
ここでは、objectを使ってもよいケースと、他の方法を選ぶべきケースを整理します。
10-1. objectを使ってもよいケース
objectを使ってもよいケースは、型が本当に限定できない場合です。
たとえば、ログ出力のように、値を文字列として表示するだけの場合です。
C#void Log(object value)
{
Console.WriteLine(value);
}
また、異なる型の値を一時的に扱う必要がある場合にも使われることがあります。
C#object[] values = { "Name", 100, true };
古いAPIやフレームワークとの連携で、object型を使う必要がある場合もあります。
C#void SomeOldApi(object parameter)
{
// 古いAPIの例
}
このように、型を固定できない明確な理由がある場合には、objectを使っても問題ありません。
10-2. ジェネリクスを使うべきケース
同じ処理を複数の型で使いたい場合は、まずジェネリクスを検討しましょう。
objectを使うと、型情報が失われやすく、キャストが必要になります。
C#object GetValue()
{
return "Hello";
}
string text = (string)GetValue();
ジェネリクスを使えば、型を保ったまま扱えます。
C#T GetValue<T>(T value)
{
return value;
}
string text = GetValue("Hello");
int number = GetValue(100);
コレクションでも同じです。
C#List<object> values = new List<object>();
values.Add("Hello");
string text = (string)values[0];
文字列だけを扱うなら、List<string>を使うべきです。
C#List<string> values = new List<string>();
values.Add("Hello");
string text = values[0];
型が決まっている場合や、型安全性を保ちたい場合は、objectよりジェネリクスが適しています。
10-3. インターフェースを使うべきケース
複数のクラスに共通の機能がある場合は、objectではなくインターフェースを使うべきです。
たとえば、通知を送る処理を考えます。
C#interface INotifier
{
void Send(string message);
}
メール通知とチャット通知を実装します。
C#class EmailNotifier : INotifier
{
public void Send(string message)
{
Console.WriteLine($"メール送信: {message}");
}
}
class ChatNotifier : INotifier
{
public void Send(string message)
{
Console.WriteLine($"チャット送信: {message}");
}
}
この場合、メソッドの引数はobjectではなくINotifierにします。
C#void Notify(INotifier notifier)
{
notifier.Send("お知らせです");
}
objectにしてしまうと、Sendメソッドを直接呼び出せません。
C#void Notify(object notifier)
{
// notifier.Send("お知らせです"); は書けない
}
共通の振る舞いを持つものを扱うなら、インターフェースを使う方が安全で読みやすいです。
10-4. dynamicを検討するケース
dynamicは、実行時にメソッドやプロパティを解決したい場合に使います。
たとえば、外部から受け取った動的なデータを扱う場合などです。
C#dynamic value = "Hello";
Console.WriteLine(value.Length);
objectでは、キャストしなければLengthにアクセスできません。
C#object value = "Hello";
// Console.WriteLine(value.Length); はコンパイルエラー
ただし、dynamicはコンパイル時の型チェックが弱くなるため、実行時エラーが発生しやすくなります。
C#dynamic value = "Hello";
// 実行時エラー
// value.NotFoundMethod();
そのため、dynamicは必要な場面に限定して使うべきです。
通常のアプリケーション開発では、具体的な型、ジェネリクス、インターフェースで表現できるかを先に考えましょう。
10-5. 初心者が判断に迷ったときの考え方
初心者がobjectを使うべきか迷ったときは、次のように考えると判断しやすくなります。
まず、扱う型が明確なら具体的な型を使います。
C#string name = "Taro";
int age = 20;
同じ処理を複数の型で使いたいなら、ジェネリクスを検討します。
C#void Print<T>(T value)
{
Console.WriteLine(value);
}
共通の機能を持つ複数のクラスを扱いたいなら、インターフェースを使います。
C#void Execute(IRunnable item)
{
item.Run();
}
どうしても型が特定できない場合や、古いAPIとの連携が必要な場合にobjectを使います。
C#void Process(object value)
{
Console.WriteLine(value);
}
基本方針としては、「最初からobjectを選ぶ」のではなく、「具体的な型で表現できないか」を先に考えるのがおすすめです。
11. C# objectに関するよくある質問
ここでは、C# objectについて初心者が疑問に思いやすいポイントをQ&A形式で解説します。
11-1. object型には何でも代入できる?
基本的には、C#のほとんどの値をobject型に代入できます。
C#object a = "Hello";
object b = 123;
object c = true;
object d = new int[] { 1, 2, 3 };
自作クラスのインスタンスも代入できます。
C#class User
{
public string Name { get; set; }
}
object user = new User { Name = "Taro" };
ただし、「代入できる」ことと「そのまま自由に使える」ことは別です。
C#object value = "Hello";
// これはできない
// Console.WriteLine(value.Length);
object型として扱っている間は、objectが持つメンバーしか直接使えません。
文字列として使いたい場合は、キャストや型チェックが必要です。
C#if (value is string text)
{
Console.WriteLine(text.Length);
}
11-2. object型の中身の型を調べるには?
objectの中身の型を調べるには、GetTypeやis演算子を使います。
GetTypeを使う例です。
C#object value = 123;
Console.WriteLine(value.GetType());
出力結果は次のとおりです。
C#System.Int32
特定の型かどうかを調べるなら、isを使います。
C#object value = "Hello";
if (value is string)
{
Console.WriteLine("stringです");
}
パターンマッチングを使うと、型チェックと変換を同時に行えます。
C#object value = "Hello";
if (value is string text)
{
Console.WriteLine(text.Length);
}
nullの可能性がある場合は、GetTypeを呼ぶ前にnullチェックをしましょう。
C#object value = null;
if (value != null)
{
Console.WriteLine(value.GetType());
}
else
{
Console.WriteLine("nullです");
}
11-3. object型はnullを代入できる?
はい、object型にはnullを代入できます。
C#object value = null;
objectは参照型なので、nullを持つことができます。
ただし、nullのままメソッドを呼び出すとエラーになります。
C#object value = null;
// 実行時エラー
// value.ToString();
安全に扱うには、nullチェックを行います。
C#object value = null;
if (value == null)
{
Console.WriteLine("値はnullです");
}
else
{
Console.WriteLine(value.ToString());
}
また、null条件演算子を使うこともできます。
C#Console.WriteLine(value?.ToString() ?? "値はnullです");
11-4. object型をstringに変換するには?
objectをstringに変換する方法はいくつかあります。
中身が確実にstringだとわかっている場合は、キャストできます。
C#object value = "Hello";
string text = (string)value;
Console.WriteLine(text);
ただし、中身がstringでない場合はエラーになります。
C#object value = 123;
// 実行時エラー
// string text = (string)value;
安全に変換したい場合は、as演算子を使います。
C#object value = "Hello";
string text = value as string;
if (text != null)
{
Console.WriteLine(text);
}
文字列表現が欲しいだけなら、ToStringを使います。
C#object value = 123;
string text = value.ToString();
Console.WriteLine(text);
出力結果は次のとおりです。
C#123
nullの可能性がある場合は、次のように書くと安全です。
C#object value = null;
string text = value?.ToString() ?? "";
Console.WriteLine(text);
「中身が文字列であることを前提に取り出す」のか、「文字列表現に変換したい」のかによって使い分けましょう。
11-5. object型は実務でよく使う?
objectはC#の基本的な型なので、実務でも登場します。
ただし、自分で書くコードでは多用しない方がよいことが多いです。
実務でobjectが使われる場面には、次のようなものがあります。
C#void Log(object value)
{
Console.WriteLine(value);
}
このようなログ出力や、古いAPI、フレームワーク内部、イベント処理などでobjectを見ることがあります。
一方で、通常の業務ロジックでは、具体的な型を使う方が安全です。
C#void UpdatePrice(int price)
{
Console.WriteLine(price);
}
複数の型に対応したい場合も、ジェネリクスやインターフェースを使う方が適していることが多いです。
C#void Print<T>(T value)
{
Console.WriteLine(value);
}
そのため、実務では「必要な場面では使うが、何でもobjectにするのは避ける」という考え方が重要です。
まとめ
C#のobjectは、すべての型の基底となる重要な型です。
string、int、bool、配列、自作クラスのインスタンスなど、さまざまな値をobjectとして扱うことができます。
C#object a = "Hello";
object b = 123;
object c = true;
objectを使うと柔軟なコードを書けますが、具体的な型として使うにはキャストや型チェックが必要です。
C#object value = "Hello";
if (value is string text)
{
Console.WriteLine(text.Length);
}
また、値型をobjectに代入するとボックス化が発生し、取り出すときにはアンボックス化が必要になります。
C#int number = 10;
object obj = number; // ボックス化
int result = (int)obj; // アンボックス化
objectを使うときは、次の点に注意しましょう。
C#// 型が違うとInvalidCastExceptionの原因になる
object value = 123;
// string text = (string)value;
// nullに対するメソッド呼び出しはNullReferenceExceptionの原因になる
object empty = null;
// empty.ToString();
実務では、型が明確な場合は具体的な型を使い、複数の型に対応したい場合はジェネリクスやインターフェースを検討するのがおすすめです。
objectは便利な型ですが、使いすぎると型安全性が下がり、コードが読みにくくなります。
初心者のうちは、objectを「何でも入れられる型」とだけ覚えるのではなく、「使う場面を選ぶ必要がある型」として理解しておくことが大切です。

