値・参照同士の比較

過去のブログのアーカイブ
この記事は前身のブログのアーカイブを引き継いだものです. 画像が正しく表示できないなど,コンテンツの表示に問題がある恐れがあります.

C#の変数型には値型と参照型の2種類があることはみなさんご存知だと思います。
その2つの扱いについて少しまとめてみました。

おさらい

値型

値型は数値型や構造体(struct)によくあるもので、引数の指定とかするとデータが毎回コピーされます。
そのため、引数の指定にでかい構造体を指定するようにすると、ものすごいコストがかかるようになります。

参照型

参照型はいわゆるポインター。引数の指定とかではメモリのアドレスが渡されるだけでデータはメモリ上のどこかに存在します。
なので極端な話、1MBとかのデータを引数で渡すときも参照としてわたしてあげると、実際に渡されるデータは1,2バイトです。

文字列型について

文字列型は、一応参照型なのですが結構特殊な存在で、文字列を操作するときに毎回コピーされてから処理されます。
つまりは参照型・値型の両方を持ち合わせた存在だと言えます。
逆にstring型ではなくStringBuilder型を使うことで完全な参照型にしてあげることもできます。

ボクシング(Boxing)

もともと値型であるデータを、一応程度の参照型にしてあげることもできます。
やり方は単純で、データをobjectに変換してあげるだけ。

// なんかの構造体
StructData data = new StructData();
// ボクシング
object obj = (object)data;

ただ、これにも問題があり、巨大な構造体をボクシングするのはいいのですが、データを取り出すたびに毎回、元の型に変換して上げる必要があるため、かなりコストの高い処理といえます。
ちなみに引数で値型を参照渡しする場合はrefをつけてあげるだけです。

比較について

比較は引数に渡される時と同じ状態で比較されます。つまり、値型はデータを比較、参照型はアドレス同士で比較するわけです。

値型の場合

int val1 = 100, val2 = 200;
if (val1 == val2) // trueになる
    Console.WriteLine("true");
else
    Console.WriteLine("false");

普通の数値型で考えると簡単ですね。100と200で比較すれば当然falseになります。
この、データをそのまま比較するということでポイントで、構造体でも同じように、中身の値が同じなら別のインスタンスでもtrueになります。

参照型の場合

さて、問題の参照型。問題というほどでもないですが…初心者が陥りやすい
こちらの場合は、別のインスタンスなら内容が同じいでもfalseになります。なぜならアドレスを比較しているから。
例えば、これは別インスタンスのためfalseになります。

static void Main(string[] args) {
    ClassData data1 = new ClassData() { Val = 100 };
    ClassData data2 = new ClassData() { Val = 100 };
    if (data1 == data2)
        Console.WriteLine("true");
    else
        Console.WriteLine("false");
}
internal class ClassData
{
    internal int Val = 100;
}

あと、これはボクシングした値型にも同じように言えます。

if ((object)100 == (object)100)
    Console.WriteLine("true");
else
    Console.WriteLine("false");

こちらもfalseとなります。なぜ?それはそれぞれの100を各自でボクシングしたからアドレスが違うのです。