私は明確で簡潔で正確な答えを探しています。
良い説明へのリンクを歓迎しますが、理想的には実際の答えとして。
language-but-not-type-agnostic
?static-language-agnostic
?SOが区別を必要とするかどうかはわかりません。メタには良い質問かもしれません。
私は明確で簡潔で正確な答えを探しています。
良い説明へのリンクを歓迎しますが、理想的には実際の答えとして。
language-but-not-type-agnostic
?static-language-agnostic
?SOが区別を必要とするかどうかはわかりません。メタには良い質問かもしれません。
回答:
ボックス化された値は、プリミティブ型 *の最小ラッパーであるデータ構造です。ボックス化された値は通常、ヒープ上のオブジェクトへのポインタとして格納されます。
したがって、ボックス化された値はより多くのメモリを使用し、アクセスするために最低2回のメモリルックアップを必要とします。明らかに、これは内側のループに必要な種類ではありません。一方、ボックス化された値は、通常、システム内の他のタイプとうまく機能します。これらは言語の第一級のデータ構造であるため、他のデータ構造と同様に期待されるメタデータと構造を持っています。
JavaおよびHaskellでは、ジェネリックコレクションにボックス化されていない値を含めることはできません。.NETのジェネリックコレクションは、ペナルティなしでボックス化されていない値を保持できます。Javaのジェネリックがコンパイル時の型チェックにのみ使用される場合、.NETは実行時にインスタンス化されるジェネリック型ごとに特定のクラスを生成します。
JavaとHaskellにはボックス化されていない配列がありますが、他のコレクションよりも明らかに便利ではありません。ただし、ピーク時のパフォーマンスが必要な場合は、ボックス化とボックス化解除のオーバーヘッドを回避するのは少し不便です。
*この説明では、プリミティブ値は、ヒープ上の値へのポインターとして格納されるのではなく、呼び出しスタックに格納できる任意の値です。多くの場合、それはマシンタイプ(int、floatなど)、構造体、場合によっては静的サイズの配列だけです。.NETランドは、それらを(参照型ではなく)値型と呼びます。Javaの人々はそれらをプリミティブ型と呼びます。Haskellionsは、それらをunboxedと呼びます。
**この回答では、Java、Haskell、C#にも焦点を当てています。価値があるのは、Python、Ruby、Javascriptのすべてに、ボックス化された値だけが含まれていることです。これは「すべてがオブジェクトである」アプローチとしても知られています***。
***警告:十分に高度なコンパイラー/ JITは、ソースを見るときに意味的にボックス化された値を、実行時にボックス化解除された値として安全に検出できる場合があります。本質的に、見事な言語の実装者のおかげで、ボックスが無料になる場合があります。
C#3.0から:
ボクシングとは、値型を参照型にキャストすることです。
int x = 9;
object o = x; // boxing the int
unboxingは...逆です:
// unboxing o
object o = 9;
int x = (int)o;
ボクシングとアンボクシングは、プリミティブ値をオブジェクト指向ラッパークラスに変換する(ボクシング)、またはオブジェクト指向ラッパークラスの値をプリミティブ値に変換する(アンボクシング)プロセスです。
たとえば、Javaでは、プリミティブはオブジェクトにのみ格納できないため、int
値をInteger
に格納する場合は、値を(ボクシング)に変換する必要があります。しかし、それを元に戻したい場合は、値をとしてではなくとして取得したい場合があります。Collection
Collection
Collection
int
Integer
ボクシングとアンボクシングは本質的に悪いわけではありませんが、トレードオフです。言語の実装によっては、プリミティブを使用する場合よりも処理が遅くなり、メモリを大量に消費する可能性があります。ただし、より高いレベルのデータ構造を使用して、コードの柔軟性を高めることもできます。
最近では、Java(および他の言語)の「オートボクシング/自動アンボクシング」機能のコンテキストで最もよく議論されています。これはオートボクシングのJava中心の説明です。
.Netの場合:
多くの場合、関数が消費する変数のタイプに依存できないため、最も一般的な分母から拡張されたオブジェクト変数を使用する必要があります。 object
。
ただしobject
、クラスであり、その内容を参照として保存します。
List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value
List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int
これらは両方とも同じ情報を保持しますが、2番目のリストは大きく、低速です。2番目のリストの各値は、実際にobject
は、int
です。
int
によってラップされるため、これはボックス化と呼ばれますobject
。そのキャストバック時int
と、ボックス化が解除され値に変換されます。
値タイプ(つまり、すべて structs
)の場合、これは低速で、潜在的に多くのスペースを使用します。
参照タイプ(つまり、すべて classes
)の場合、参照として保存されるため、これは問題にはなりません。
ボックス化された値タイプのもう1つの問題は、値ではなくボックスを処理していることが明らかでないことです。2つを比較structs
すると値を比較しますが、2つを比較するとclasses
(デフォルトでは)参照を比較します。つまり、これらは同じインスタンスですか?
これは、ボックス化された値タイプを処理するときに混乱する可能性があります。
int a = 7;
int b = 7;
if(a == b) // Evaluates to true, because a and b have the same value
object c = (object) 7;
object d = (object) 7;
if(c == d) // Evaluates to false, because c and d are different instances
回避するのは簡単です:
if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals
if(((int) c) == ((int) d)) // Evaluates to true once the values are cast
ただし、ボックス化された値を処理する場合は、もう1つ注意が必要です。
Object
あり、等価演算子は実装されていませんが、クラス型はIs
演算子と比較できます。逆に、Int32
等号演算子と一緒に使用できますが、は使用できませんIs
。この違いにより、どのタイプの比較が行われているかがより明確になります。
.NET FCLジェネリックコレクション:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
以前のコレクションの実装におけるボックス化とボックス化解除のパフォーマンスの問題を克服するために設計されました。
詳細については、Chapter 16、CLR via C#(2nd Edition)を参照してください。
ボックス化とボックス化解除により、値タイプをオブジェクトとして扱うことが容易になります。ボクシングとは、値をオブジェクト参照型のインスタンスに変換することを意味します。たとえば、Int
はクラスであり、int
はデータ型です。変換int
するにInt
変換する一方、ボクシングの例示であるInt
とすると、int
アンボクシングです。一方、このコンセプトはガベージコレクションに役立ちます。ボックス化解除は、オブジェクトタイプを値タイプに変換します。
int i=123;
object o=(object)i; //Boxing
o=123;
i=(int)o; //Unboxing.
var ii = 123; typeof ii
返しますnumber
。var iiObj = new Number(123); typeof iiObj
を返しますobject
。typeof ii + iiObj
を返しますnumber
。したがって、これはボクシングと同等のJavaScriptです。値iiObjは、算術を実行してボックス化されていない値を返すために、プリミティブ番号(ボックス化されていない)に自動的に変換されました。
他と同様に、オートボクシングは慎重に使用しないと問題が発生する可能性があります。古典的には、NullPointerExceptionが発生し、追跡することができません。デバッガでも。これを試して:
public class TestAutoboxNPE
{
public static void main(String[] args)
{
Integer i = null;
// .. do some other stuff and forget to initialise i
i = addOne(i); // Whoa! NPE!
}
public static int addOne(int i)
{
return i + 1;
}
}
i
は早期に初期化されます。空の宣言にする(Integer i;
)にして、初期化を忘れたことをコンパイラーが指摘できるようにするか、その値がわかるまで宣言を待機します。