C#で値型をボックス化するためのユースケース?
-
06-07-2019 - |
質問
のインスタンスが 値型は 参照タイプのインスタンス。 このような状況、値型 インスタンスはに変換できます を介した参照型インスタンス ボクシングと呼ばれるプロセス。値が タイプインスタンスはボックス化され、ストレージは ヒープに割り当てられ、 インスタンスの値はそれにコピーされます スペース。このストレージへの参照は スタックに配置されます。箱入りの値 オブジェクトであり、参照タイプ 値の内容が含まれています インスタンスを入力します。
ウィキペディアには、Javaの例があります。しかし、C#では、値型をボックス化する必要がある場合はどうなりますか?または、より良い/同様の質問は、なぜスタックではなくヒープ(ボックス化された)に値型を保存したいのでしょうか?
解決
一般に、通常、値型のボックス化は避けたいでしょう。
ただし、これが役立つ場合はまれにしか発生しません。たとえば、1.1フレームワークをターゲットにする必要がある場合、汎用コレクションにアクセスできません。 .NET 1.1でコレクションを使用するには、値の型をSystem.Objectとして扱う必要があります。これにより、ボクシング/アンボクシングが発生します。
.NET 2.0+でこれが役立つ場合がまだあります。値型を含むすべての型をオブジェクトとして直接扱うことができるという事実を利用したいときはいつでも、ボクシング/アンボクシングを使用する必要があるかもしれません。これは、コレクション内の任意の型を保存できるため(汎用コレクションでTの代わりにオブジェクトを使用することにより)便利な場合がありますが、一般に、型の安全性が失われるため、これを回避する方がよいでしょう。ただし、ボクシングが頻繁に発生するケースの1つは、リフレクションを使用している場合です。リフレクションでの呼び出しの多くは、値のタイプを事前に知らないため、値のタイプを操作するときにボクシング/アンボクシングを必要とします。
他のヒント
値の型を意図的にボックス化する正当な理由はほとんどありません。ほとんどの場合、値型をボックス化する理由は、型を認識しないコレクションに値を格納するためです。たとえば、古い ArrayList はコレクションです参照タイプであるオブジェクトの。整数などを収集する唯一の方法は、それらをオブジェクトとしてボックス化し、ArrayListに渡すことです。
最近、一般的なコレクションがあるので、これはそれほど問題ではありません。
通常、ボクシングは必要なときに.NETで自動的に行われます。多くの場合、参照型を期待するものに値型を渡すときに。一般的な例はstring.Format()です。このメソッドにプリミティブ値型を渡すと、呼び出しの一部としてボックス化されます。だから:
int x = 10;
string s = string.Format( "The value of x is {0}", x ); // x is boxed here
これは、値型(x)が自動的にボックス化されて、オブジェクトを予期するメソッドに渡される単純なシナリオを示しています。一般的に、可能な場合は値の型をボックス化しないようにしますが、場合によっては非常に便利です。
興味深いことに、.NETでジェネリックを使用する場合、パラメーターまたは型のメンバーとして使用する場合、値の型はボックス化されません。これにより、すべてを{オブジェクト}として扱い、型に依存しない古いC#コード(ArrayListなど)よりもジェネリックが効率的になります。これにより、List<T>
やDictionary<T,K>
よりもArrayList
やHashtable
などの汎用コレクションを使用する理由がもう1つ追加されます。
Eric Lippertの素晴らしい記事を2つお勧めします
http ://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx
これは、私が100%同意するという引用です
値のローカルのスタックの使用 タイプは、 CLRはあなたに代わって実行します。 値型の関連機能は 彼らは存在の意味を持っていること 値によってコピーされます 割り当て解除は、以下によって最適化できます。 ランタイム。
99%のアプリケーションでは、開発者は、値型がヒープではなくスタックにある理由や、ここでどのようなパフォーマンス向上が得られるのかを気にする必要はありません。 Jutsは非常に単純なルールを念頭に置いています:
- ボクシング/ボクシング解除を避ける 必要に応じて、ジェネリックコレクションを使用します。 ほとんどの問題は、あなたが 独自のタイプを定義しますが、 既存のタイプを不適切に使用する (Microsoftまたは 同僚)
- 値型を作成する シンプル。構造体が必要な場合 10〜20個のフィールドがある場合、 クラスを作成します。想像してみて そのフィールドは毎回コピーされます あなたが時々それを渡すとき 値による関数...
- 持っていることはあまり有用ではないと思う 参照型を持つ値型 内部のフィールド。構造体のように 文字列フィールドとオブジェクトフィールド。
- 必要に応じて必要なタイプを定義する どこでもではなく、必要な機能 保存する必要があります。構造体は と比較して制限された機能 クラス。したがって、structが提供できない場合 必要な機能、たとえば デフォルトのコンストラクタ、クラスを定義します。
- 何かが実行できる場合 他のデータを使用したアクション タイプ、それは通常として定義されます クラス。構造体操作の場合 異なるタイプを定義する必要があります 1つの型をキャストできる場合のみ 別の。 intを追加できるとしましょう doubleにintをキャストできるため ダブル。
- 何かがステートレスでなければならない場合、それはクラスです。
- 迷っているときは、参照型を使用します。 :-)
特別な場合にルールは除外を許可しますが、過剰に最適化を試みないでください。
p.s。 スタックとヒープの違いを知らない2-3年の経験を持つASP.NET開発者に会いました。 :-(私がインタビュアーならそのような人を雇うつもりはないが、私が今まで見たどのASP.NETサイトでもボクシング/アンボクシングがボトルネックになる可能性があるからではない。
C#でのボクシングの良い例は、 ArrayList 。
1つの例は、メソッドがオブジェクトパラメータを受け取り、値の型を渡す必要がある場合です。
以下はボクシング/アンボクシングの例です
ArrayList ints = new ArrayList();
myInts.Add(1); // boxing
myInts.Add(2); // boxing
int myInt = (int)ints [0]; // unboxing
Console.Write("Value is {0}", myInt); // boxing
これが発生する状況の1つは、たとえば、オブジェクト型のパラメーターを予期するメソッドがあり、プリミティブ型の1つ、たとえばintを渡す場合です。または、パラメーターをint型の 'ref'として定義する場合。
コード
int x = 42;
Console.Writeline("The value of x is {0}", x );
Writeline
は内部でint
キャストを行うため、実際にはボックス化およびボックス化解除します。これを回避するには、次のようにします
int x = 42;
Console.Writeline("The value of x is {0}", x.ToString());
微妙なバグに注意してください!
struct
として独自の型を宣言することにより、独自の値型を宣言できます。多数のプロパティでArrayList
を宣言し、[]
内にいくつかのインスタンスを配置するとします。これはもちろんそれらを囲みます。次に、readonly
演算子を使用して参照し、それを型にキャストして、プロパティを設定します。 コピーにプロパティを設定するだけです。 <=>内のものは変更されていません。
このため、値型は常に不変である必要があります。つまり、コンストラクターでのみ設定でき、メンバーとして可変型を持たないように、すべてのメンバー変数を<=>にします。