梱包と開梱
参照型オブジェクトについて話すとき、それが全能であるとは言いますが、どこが全能であるかについては述べません。
各変数型に対して ToString、GetHashCode、Equals、および GetType メソッドを提供する object に加えて、object はすべての型の親クラスであり、任意の変数型を object に変換できます。
一方で、オブジェクト タイプを使用すると、明示的に次のことが可能になります。に変換任意のタイプ (ボックス化またはボックス化解除が行われない場合):
object arr=new int[10];
int[] ar=(int[])arr;
object arr=new int[10];
int[] ar=arr as int[]; // as强转object类型为int数组
一方、値の型を変換します。に変換オブジェクト (参照型)の処理をボックス化といい、ボックス化されたオブジェクトを値型に戻す処理をアンボックス化といいます。
次の例では、整変数 i をボックス化し、それをオブジェクト o に割り当てます。
int i = 123;
// The following line boxes i.
object o = i; //隐式装箱
次に、オブジェクト o をボックスから取り出し、整数変数 i に割り当てることができます。
o = 123;
i = (int)o; // unboxing
以下は、オブジェクトのリストを使用してさまざまな値タイプのデータを保存し、それぞれを操作する方法を示す公式の例です。
Console.WriteLine(String.Concat("Answer", 42, true));
List<object> mixedList = new List<object>();
mixedList.Add("First Group:");
for (int j = 1; j < 5; j++)
{
mixedList.Add(j);
}
mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
mixedList.Add(j);
}
foreach (var item in mixedList)
{
Console.WriteLine(item);
}
var sum = 0;
for (var j = 1; j < 5; j++)
{
sum += (int)mixedList[j] * (int)mixedList[j];
// 注意在进行数值运算的时候不能用object直接运算,应当拆箱为原类型再计算
}
Console.WriteLine("Sum: " + sum);
// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30
パフォーマンスの消費
単純な割り当てと比較して、ボックス化およびボックス化解除のプロセスでは多くの計算が必要になります。値型をボックス化する場合、新しいオブジェクトを割り当てて構築する必要があります。アンボックス化に必要なキャストも、程度は低いものの、大量の計算を要します。
実際、絶対に必要な場合を除き、プログラミングではボックス化とボックス化解除は避けるべきです。
変数をボックス内で参照した後にボックス化を解除する必要がある場合は、新しい変数を直接割り当てることをお勧めします。これは、値の型をボックス化するときに、まったく新しいオブジェクトを作成する必要があり、単純な参照割り当てよりも時間がかかる可能性があるためです。 . 20回。開梱プロセスには、割り当て操作の最大 4 倍の時間がかかる場合があります。
変数またはメソッドで頻繁なボックス化とボックス化解除が必要な場合は、プログラミング自体に問題があることを示しています。たとえば、任意の入力型を受け入れる関数が定義されていますが、すべての入力値型はメソッド内でオブジェクト型に強制されます。上の例で定義されているようにList<object>
、値を保存するたびに値はボックス化されます。実際には、ジェネリックを定義する方がList<T>
良い選択であり、任意の値型の変数を受け取る必要がある場合は、他の型のオブジェクト ボックス化の代わりにジェネリックを使用する必要があります。たとえば、次の例では、明らかに前者の方が優れています。
public class Stack<T>
{
List<T> a = new List<T>();
}
public class Stack
{
List<object> b = new List<object>();
}
パッキング
ボクシングは、ガベージ コレクションされたヒープに値の型を格納するために使用されます。ボックス化は、値型からオブジェクト型、またはこの値型によって実装されるインターフェイス型への暗黙的な変換です。値型をボックス化すると、ヒープ上にオブジェクト インスタンスが割り当てられ、その値が新しいオブジェクトにコピーされます。
このステートメントの結果、スタック上にオブジェクト参照 o が作成され、ヒープ上に int 型の値が作成されます。値は、変数 i に割り当てられた値型の値のコピーです。次のビン化された変換プロットは、2 つの変数 i と o の違いを示しています。
(上の図からわかるように、i は元々値型としてスタックに格納され、object は参照型としてヒープに格納されます。ボックス化操作を実行すると、一方で、値の型はヒープ上に記録され、ヒープ上のオブジェクトの値は 123 に割り当てられ、ヒープ上のオブジェクトを参照する参照 o がスタック上に作成されます。)
これは、ボックス化中、オブジェクトは i の値のみをコピーし、そのアドレス自体はコピーしないことを意味します。アドレスは値型が割り当てられるときに再作成され、参照値型は信頼できないため、これも簡単に理解できます。
class TestBoxing
{
static void Main()
{
int i = 123;
// Boxing copies the value of i into object o.
object o = i;
// Change the value of i.
i = 456;
// The change in i doesn't affect the value stored in o.
System.Console.WriteLine("The value-type value = {0}", i);
System.Console.WriteLine("The object-type value = {0}", o);
}
}
/* Output:
The value-type value = 456
The object-type value = 123
*/
開梱する
アンボックス化は、オブジェクト型から値型へ、またはインターフェイス型からインターフェイスを実装する値型への明示的な変換です。開梱操作には次のものが含まれます。
-
オブジェクト インスタンスをチェックして、指定された値タイプのボックス化された値であることを確認します。
-
インスタンスの値を値型変数にコピーします。
ボックス化解除プロセスからわかるように、実際にボックス化するときは、まずボックス化解除項目 o がオブジェクトへの参照であるかどうかを判断します。次に、アンボックス化タイプがオブジェクトのボックス化タイプであるかどうかが判断され、最終的にアンボックス化された値がスタック上に再作成されます。
さらに、ボックス化解除時に暗黙的な変換もありますが、次の例のように、ボックス化解除項目のタイプは同じである必要があります。
float t =(int) o; //拆箱项正确,拆出的int可直接隐式转换为folat
int t =(float) o; //拆箱项错误
上記の原則によれば、ボックス化されたオブジェクトの値を変更する場合、唯一の方法は、まずボックス化を解除してから再ボックス化することです。
var、object、dynamic、<T> を比較します。
これら 4 つのメソッドは似ており、任意の型を受け取ることができますが、実際には大きく異なります。
だった
var の原理はコンパイラに基づいており、var を使用して変数の型を定義する場合、var はローカル変数にのみ使用でき、コンパイラは初期化式から変数の型を推測できます。
var a = 12;
a.IndexOf("1", 0, 2); //报错,编译器已经推测出a是int类型了,不能使用string的方法
var の一般的な使用法は、コンストラクター呼び出し式です。関数内の一部のローカル変数の代入タイプが不明な場合、var を使用してそれらを受け取るのが安全です。var 型を使用するのが最も便利でカジュアルです。
物体
オブジェクトは任意の型に変換できますが、ボックス化によって値の型変換を受けることもできます。利点は、オブジェクト型を関数の戻り値の型として使用できること、または関数の入力パラメーターでオブジェクト クラスを定義できることです。しかし、その利点は欠点でもあり、オブジェクトパラメータが関数内で定義されていると仮定すると、他の人がその関数を使用するときに何が渡されるかは誰にもわかりませんし、状況はますます複雑になります。ジェネリックスを使用するか、パラメーターの型を指定して関数内でボックス化します。
したがって、型変換の場合はオブジェクトのみを使用する方がよいでしょう。関数内で可変型を定義し、ボックス化とボックス化解除を発生させたくない場合は、<T> を使用します。
<T> ジェネリック
ジェネリックは変数を除くすべてを定義できます。独自の汎用インターフェイス、汎用クラス、汎用メソッド、汎用イベント、および汎用デリゲートを作成できます。ジェネリック クラスは、特定のデータ型のメソッドにアクセスするように制約することもできます。
ジェネリックスの簡単な例は、コレクション クラスによって提供されますList<T>
。これがどれほど便利かは言うまでもありません。
ジェネリックを使用して、さまざまなクラスまたはメソッドで任意の型を取得します。ジェネリック医薬品はボックス化またはボックス化解除する必要がありません。ジェネリックを置換として理解することができ、使用時にジェネリック パラメータを特定の型に置き換えることができます。このプロセスはコンパイル中に実行されます。ジェネリックを使用しても、コンパイラは型エラーを検出できます。
動的
Dynamic 自体もオブジェクトです。ほとんどの場合、動的型はオブジェクト型と同様に動作します。具体的には、Null 以外の式を動的型に変換できます。動的型は、コンパイラが動的型を含む式の解析や型チェック操作を行わない点でオブジェクトとは異なります。コンパイラは操作に関する情報をパッケージ化し、実行時に操作を評価するために使用します。このプロセス中に、動的型変数はオブジェクト型変数にコンパイルされます。したがって、動的型はコンパイル時にのみ存在し、実行時には存在しません。
利点は、dynamic は、ほとんどのスクリプト言語と同様に動的変数であることです。一方で、特定の動的用途に便利であり、他方、スクリプト言語と接続することもできます。
ただし、欠点も明らかであり、これを使用して作成されたプログラムをデバッグするのがそれほど簡単ではない場合があります。同時に、dynamic を関数のパラメーターや戻り値として使用しないでください。object よりも厄介な結果が生じるだけです。