.NET の型は、クラス(class/Class)と構造体(struct/Structure)の2つに大きく分けられる。
クラスは参照型とも呼ばれ、その変数はデータがあるメモリへのアドレス(参照)を保持している。
構造体は値型とも呼ばれ、その変数はデータを直接保持している。
クラスのデータが置かれる場所をヒープ、構造体のデータが置かれる場所をスタックと呼ぶ。
クラスと構造体は両方ともデータとメソッドを持てるので、慣れないうちはどちらを使えばいいか迷うと思う。
とりあえず、使い分けの基準になりそうな違いを挙げる。
List<T> 使用時の間違い易いパターン。
Nullable<T> は下記のように定義されてる。
つまり、シンタックスシュガーになっている。
ちょっと反則っぽいが・・・
ちなみに、es を object 変数に代入し、その変数を ElementS でキャストして Value を変更するとかはできない。
コンパイルエラーになる。(コンパイラ エラー CS0445)
クラスは参照型とも呼ばれ、その変数はデータがあるメモリへのアドレス(参照)を保持している。
構造体は値型とも呼ばれ、その変数はデータを直接保持している。
クラスのデータが置かれる場所をヒープ、構造体のデータが置かれる場所をスタックと呼ぶ。
クラスと構造体は両方ともデータとメソッドを持てるので、慣れないうちはどちらを使えばいいか迷うと思う。
とりあえず、使い分けの基準になりそうな違いを挙げる。
-
代入(引数への値渡し)時と、ボックス化&ボックス化解除(キャスト)時、構造体はデータをコピーする
クラスはアドレスのコピーのみ
※ 構造体のサイズが大きいほど、代入のコストは大きくなる -
構造体は null にならない
※ null とはクラス変数がデータ(参照先)を持ってないことを表すアドレス - インスタンス作成(new)の速度は、クラスより構造体の方が速い
- 構造体は GC の対象にならない (スコープから外れた時点で、自動的に消える)
- 構造体は他の構造体やクラスから継承できず、継承元にもならない (ただし、インターフェースは実装可能)
補足
- についてコードで説明
class ElementC
{
public ElementC(int value) { Value = value; }
public int Value;
}
struct ElementS
{
public ElementS(int value) { Value = value; }
public int Value;
}
static class Program
{
static void Main(string[] args)
{
/* クラス変数の代入 */
var ec = new ElementC(1);
Console.WriteLine(ec.Value); // 1
var ec_ = ec; /* クラスの代入はアドレスのコピー */
ec_.Value = 2; /* 同じデータを指しているので ec_ を変更すると ec も変わる */
/* 結果 */
Console.WriteLine(ec_.Value); // 2
Console.WriteLine(ec.Value); // 2
/* 構造体変数の代入 */
var es = new ElementS(10);
Console.WriteLine(es.Value); // 10
var es_ = es; /* 構造体の代入はデータのコピー */
es_.Value = 20; /* コピーなので es_ を変更しても es には変わらない */
/* 結果 */
Console.WriteLine(es_.Value); // 20
Console.WriteLine(es.Value); // 10
}
}
引数の値渡しも代入と同様。「=」が無いため、意識しにくいかも。List<T> 使用時の間違い易いパターン。
/* ElementC, ElementS の定義は上と同じ */
static class Program
{
static void Main(string[] args)
{
/* クラス変数の値渡し */
var ecs = new List<ElementC>();
var ec = new ElementC(1);
ecs.Add(ec); /* アドレスのコピー */
Console.WriteLine(ecs[0].Value); // 1
ec.Value = 2; /* ec を変更すると ecs[0] も変わる */
/* 結果 */
Console.WriteLine(ec.Value); // 2
Console.WriteLine(ecs[0].Value); // 2
/* 構造体変数の値渡し */
var ess = new List<ElementS>();
var es = new ElementS(10);
ess.Add(es); /* データのコピー */
Console.WriteLine(ess[0].Value); // 10
es.Value = 20; /* es を変更しても ess[0] は変わらない*/
/* 結果 */
Console.WriteLine(es.Value); // 20
Console.WriteLine(ess[0].Value); // 10
}
}
- について
Nullable<T> は下記のように定義されてる。
public struct Nullable<T> where T : struct
{
private bool hasValue;
internal T value;
/* 以降、省略 */
}
Nullable<T> が null を代入できたり、null と比較できたりするのは、コンパイラが該当する処理に置き換えているためである。つまり、シンタックスシュガーになっている。
その他の違い
下記が分かり易くまとめられてる。おまけ
unsafe を使えば、構造体のアドレスを取得できる。ちょっと反則っぽいが・・・
/* ElementS の定義は上と同じ */
static class Program
{
static void Main(string[] args)
{
var es = new ElementS(10);
Console.WriteLine(es.Value); // 10
unsafe {
var esp = &es; /* es のアドレス取得 */
esp->Value = 30; /* esp の参照先を変更すると、es も変わる */
Console.WriteLine(esp->Value); // 30
}
Console.WriteLine(es.Value); // 30
}
}
ちなみに、es を object 変数に代入し、その変数を ElementS でキャストして Value を変更するとかはできない。
コンパイルエラーになる。(コンパイラ エラー CS0445)
0 件のコメント:
コメントを投稿