データ型 (C# と Java の比較)
更新 : 2007 年 11 月
ここでは、データの表現、割り当て、およびガベージ コレクションに関して、Java と C# の主な類似点と相違点を説明します。
複合データ型
クラスという概念は Java でも C# でも、フィールド、メソッド、およびイベントを含む複合データ型を意味します (クラスの継承については、別途「継承と派生クラス (C# と Java の比較)」で説明します)。C# では、継承をサポートしないスタック割り当て複合データ型として構造体の概念を導入しています。その他のほとんどの点で、構造体はクラスによく似ています。構造体を使用すると、関連するフィールドとメソッドを負荷の小さい方法でグループ化し、これらを小さなループやパフォーマンスが重視されるその他のシナリオで使用できます。
C# では、クラスのインスタンスのガベージ コレクションを実行する前に呼び出すデストラクタ メソッドを作成できます。Java では、 finalize メソッドを使用して、オブジェクトのガベージ コレクションを実行する前にリソースをクリーンアップするコードを含めることができます。C# の場合、この機能は、クラス デストラクタによって実行されます。デスクトラクタは、引数を持たない、ティルダ文字 (~) で始まるコンストラクタによく似ています。
組み込みのデータ型
C# では、Java で使用できるすべてのデータ型を使用でき、また符号なしの数値と新しい 128 ビット高精度浮動小数点型も使用できます。
Java の各プリミティブ データ型では、コア クラス ライブラリが、Java オブジェクトとして表現されるラッパー クラスを提供します。たとえば、Int32 クラスは、int データ型をラップし、Double クラスは、double データ型をラップします。
一方、C# のプリミティブ データ型はいずれも System 名前空間のオブジェクトです。各データ型には、短い名前 (エイリアス) が与えられます。たとえば、int は System.Int32 の短い名前であり、double は System.Double の短い名前です。
C# のデータ型とそれぞれのエイリアスの一覧を次の表に示します。最初の 8 つのデータ型は、Java で使用できるプリミティブ型に対応します。ただし、Java の boolean は、C# では bool と呼ばれます。
短い名前 |
.NET クラス |
型 |
幅 |
範囲 (ビット) |
---|---|---|---|---|
byte |
符号なし整数 |
8 |
0 ~ 255 |
|
sbyte |
符号付き整数 |
8 |
-128 ~ 127 |
|
int |
符号付き整数 |
32 |
-2,147,483,648 ~ 2,147,483,647 |
|
uint |
符号なし整数 |
32 |
0 ~ 4294967295 |
|
short |
符号付き整数 |
16 |
-32,768 ~ 32,767 |
|
ushort |
符号なし整数 |
16 |
0 ~ 65535 |
|
long |
符号付き整数 |
64 |
-922337203685477508 ~ 922337203685477507 |
|
ulong |
符号なし整数 |
64 |
0 ~ 18446744073709551615 |
|
float |
単精度浮動小数点型 |
32 |
-3.402823e38 ~ 3.402823e38 |
|
double |
倍精度浮動小数点型 |
64 |
-1.79769313486232e308 ~ 1.79769313486232e308 |
|
char |
単一 Unicode 文字 |
16 |
テキストで使用される Unicode 記号 |
|
bool |
論理ブール型 |
8 |
true または false |
|
object |
他のすべての型の基本型 |
|||
string |
文字列 |
|||
decimal |
29 の有効桁数で 10 進数を表現できる正確な小数または整数型 |
128 |
±1.0 × 10e − 28 ~ ±7.9 × 10e28 |
C# では、すべてのプリミティブ データ型がオブジェクトとして表現されるので、プリミティブ データ型でオブジェクト メソッドを呼び出すことができます。以下にサンプルを示します。
static void Main()
{
int i = 10;
object o = i;
System.Console.WriteLine(o.ToString());
}
この処理は、自動ボックス化およびボックス化解除によって達成されます。詳細については、「ボックス化とボックス化解除 (C# プログラミング ガイド)」を参照してください。
定数
Java でも C# でも変数を宣言できます。変数の値はコンパイル時に指定し、実行時には変更できません。Java では final フィールド修飾子を使用してこのような変数を宣言しますが、C# では const キーワードを使用します。const に加えて、C# では、readonly キーワードを使用して、実行時に値を 1 回代入できる変数を、宣言ステートメントまたはコンストラクタで宣言することもできます。初期化後、readonly 変数の値は変更できません。readonly 変数が役立つシナリオとしては、別個にコンパイルされた複数のモジュールでバージョン番号などのデータを共有する必要があるような場合が挙げられます。たとえば、モジュール A を新しいバージョン番号で更新して再コンパイルすると、モジュール B を、再コンパイルの必要なしに同じ新しい定数値で初期化できます。
列挙型
列挙型は、C や C++ で使用する場合と同様に名前付き定数をグループ化するために使用します。列挙型は、Java では使用できません。簡単な Color 列挙型を定義する例を以下に示します。
public enum Color
{
Green, //defaults to 0
Orange, //defaults to 1
Red, //defaults to 2
Blue //defaults to 3
}
次の列挙型宣言に示すように、列挙型には整数値も割り当てることができます。
public enum Color2
{
Green = 10,
Orange = 20,
Red = 30,
Blue = 40
}
Enum 型の GetNames メソッドを呼び出して、列挙型として使用できる定数を表示するコード例を以下に示します。定数を表示した後、値を列挙型に割り当てて表示します。
class TestEnums
{
static void Main()
{
System.Console.WriteLine("Possible color choices: ");
//Enum.GetNames returns a string array of named constants for the enum.
foreach(string s in System.Enum.GetNames(typeof(Color)))
{
System.Console.WriteLine(s);
}
Color favorite = Color.Blue;
System.Console.WriteLine("Favorite Color is {0}", favorite);
System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
}
}
出力
Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 3
文字列
Java と C# の文字列型は、多少の違いはありますが、基本的に同じように動作します。文字列型は Java でも C# でも不変であり、文字列を作成した後に文字列の値を変更できません。どちらの言語でも、文字列の内容自体を変更するように見えるメソッドは、実際には返す新しい文字列を作成し、元の文字列は変更しません。文字列値を比較するプロセスは、C# と Java で異なります。Java で文字列を比較する場合、== 演算子は、既定では参照型を比較するので、文字列型で equals メソッドを呼び出す必要があります。C# では、== または != 演算子を使用して、文字列値を直接比較できます。文字列は C# では参照型ですが、== 演算子と != 演算子は、参照ではなく文字列値を既定で比較します。
C# で文字列を連結する場合は、Java の場合と同様に、文字列を連結するたびに新しい文字列クラスを作成するというオーバーヘッドを回避するために文字列型を使用しないでください。代わりに、StringBuilder クラスを使用してください。このクラスは、Java の StringBuffer クラスと同じように機能します。
リテラル文字列
C# では、文字列定数内でエスケープ シーケンス (タブ記号を表す "\t" や円記号を表す "\" など) の使用を避けることができます。エスケープ シーケンスを使用しない場合は、文字列値を割り当てる前に、@ 記号を使って逐語的文字列を宣言します。エスケープ文字を使用する方法とリテラル文字列を割り当てる方法を次の例に示します。
static void Main()
{
//Using escaped characters:
string path1 = "\\\\FileShare\\Directory\\file.txt";
System.Console.WriteLine(path1);
//Using String Literals:
string path2 = @"\\FileShare\Directory\file.txt";
System.Console.WriteLine(path2);
}
変換とキャスト
データ型の自動変換とキャストでは、Java も C# も同様の規則に従います。
Java と同様に C# も、暗黙の型変換と明示的な型変換の両方をサポートします。拡大変換の場合は、暗黙の変換です。たとえば、次のような int から long への変換は、Java の場合と同様に暗黙の変換です。
int int1 = 5;
long long1 = int1; //implicit conversion
.NET Framework データ型間の暗黙の型変換の一覧を次に示します。
変換元の型 |
変換先の型 |
---|---|
Byte |
short、ushort、int、uint、long、ulong、float、double、decimal |
SByte |
short、int、long、float、double、decimal |
Int |
long、float、double、decimal |
Uint |
long、ulong、float、double、decimal |
Short |
int、long、float、double、decimal |
Ushort |
int、uint、long、ulong、float、double、decimal |
Long |
float、double、decimal |
Ulong |
float、double、decimal |
Float |
double |
Char |
ushort、int、uint、long、ulong、float、double、decimal |
明示的に変換する式は、次のように Java と同じ構文を使ってキャストします。
long long2 = 5483;
int int2 = (int)long2; //explicit conversion
明示的な変換を次の表に示します。
変換元の型 |
変換先の型 |
---|---|
Byte |
sbyte または char |
SByte |
byte、ushort、uint、ulong、char |
Int |
sbyte、byte、short、ushort、uint、ulong、char |
Uint |
sbyte、byte、short、ushort、int、char |
Short |
sbyte、byte、ushort、uint、ulong、char |
Ushort |
sbyte、byte、short、char |
Long |
sbyte、byte、short、ushort、int、uint、ulong、char |
Ulong |
sbyte、byte、short、ushort、int、uint、long、char |
Float |
sbyte、byte、short、ushort、int、uint、long、ulong、char、decimal |
Double |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float、decimal |
Char |
sbyte、byte、short |
Decimal |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double |
値型と参照型
C# では、次の 2 種類の変数型を使用できます。
値型
char、int、float などの組み込みのプリミティブ データ型と、構造体で宣言されるユーザー定義型があります。
参照型
クラス、およびプリミティブ型から作成されるその他の複合データ型。参照型の変数には、型のインスタンスは含まれず、インスタンスへの参照だけが含まれます。
i と j の 2 つの値型変数を次のように作成した場合、i と j は互いに独立した別個の変数になります。
int i = 10;
int j = 20;
これらの変数には、それぞれ別々のメモリ位置が割り当てられます。
これら 2 つの変数のうち一方の値を変更しても、もう一方の値には影響しません。たとえば、次のような式が存在する場合でも、変数どうしは結び付けられません。
int k = i;
つまり、i の値を変更しても、k の値は、i の最初に割り当てられた値のまま変わりません。
i = 30;
System.Console.WriteLine(i.ToString()); // 30
System.Console.WriteLine(k.ToString()); // 10
ただし、参照型の動作は異なります。たとえば、2 つの変数を次のように宣言したとします。
Employee ee1 = new Employee();
Employee ee2 = ee1;
この場合、C# ではクラスは参照型なので、ee1 は、Employee への参照として認識されます。この 2 行のうちの最初の行によって、Employee のインスタンスがメモリに作成され、そのインスタンスへの参照として ee1 が設定されます。そのため、ee2 に ee1 を代入すると、b には、メモリ内にクラスへの参照の複製が格納されます。これら 2 つの変数は、次に示すようにメモリ内の同じオブジェクトを指すため、ee2 でプロパティを変更すると、同じ変更内容が ee1 のプロパティに反映されます。
ボックス化とボックス化解除
値型を参照型に変換する処理をボックス化と呼び、逆に参照型を値型に変換する処理をボックス化解除と呼びます。この 2 つの処理を次のコードに示します。
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
Java の場合、このような変換は手動で行う必要があります。このようなオブジェクトを作成する (ボックス化) ことで、プリミティブ データ型をラッパー クラスのオブジェクトに変換できます。また、ボックス化解除のオブジェクトで適切なメソッドを呼び出す (ボックス化解除) ことで、ラッパー クラスのオブジェクトからプリミティブ データ型の値を抽出できます。ボックス化とボックス化解除の詳細については、「ボックス化とボックス化解除 (C# プログラミング ガイド)」を参照してください。