演習 - Clear() と Resize() について詳しく調べる
引き続き、物流会社のパレット トラッカーの作成で、新しいパレットを追跡し、古いパレットを追跡から削除する必要もあるものとします。 パレットを追加および削除するための追跡機能を作成するにはどうすればよいですか?
配列メソッドを使用して配列のクリアとサイズ変更を行う
Array.Clear()
メソッドを使用すると、配列内の特定の要素の内容を削除し、配列の既定値に置き換えることができます。 たとえば、string
配列内では、int
配列要素をクリアすると、クリアされた要素の値がnull
に置き換えられます。置換は 0
(ゼロ) で行われます。
この Array.Resize()
メソッドは、配列の要素を追加または削除します。
前の演習のコードをすべて削除するか、行コメント演算子
//
を使ってコメントアウトします。Visual Studio Code エディターで次のようにコードを更新します。
string[] pallets = { "B14", "A11", "B12", "A13" }; Console.WriteLine(""); Array.Clear(pallets, 0, 2); Console.WriteLine($"Clearing 2 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); }
少し時間を取って、コード
Array.Clear(pallets, 0, 2);
の行に注目します。ここでは、
Array.Clear()
メソッドを使用して、pallets
配列のインデックス0
から始まる2
個の要素に格納されている値をクリアします。Visual Studio Code の [ファイル] メニューで、[保存] を選択します。
コードをビルドまたは実行する前に、Program.cs ファイルを保存する必要があります。
[エクスプローラー] パネルで、TestProject フォルダーの場所にあるターミナルを開くには、TestProject を右クリックし、[統合ターミナルで開く] を選択します。
ターミナル パネルが開き、ターミナルが TestProject フォルダーの場所に対して開かれていることを示すコマンド プロンプトが含まれているはずです。
コードを実行するには、ターミナルのコマンド プロンプトで、「dotnet run」と入力し、Enter キーを押します。
Note
"実行するプロジェクトが見つかりませんでした" というメッセージが表示された場合は、ターミナルのコマンド プロンプトに、予期されている TestProject フォルダーの場所が表示されていることを確かめます。 例:
C:\Users\someuser\Desktop\csharpprojects\TestProject>
コードを実行すると、配列の最初の 2 つの要素に格納されていた値がクリアされたことがわかります。
Length
プロパティとforeach
ステートメントには、要素はまだ存在していますが、空になっています。Clearing 2 ... count: 4 -- -- -- B12 -- A13
空の文字列と null
Array.Clear()
を使用すると、クリアされた要素ではメモリ内の文字列が参照されなくなります。 実際、要素では何もポイントされていません。 何もポイントされていないというのは、最初は理解しにくいかもしれない重要な概念です。
Array.Clear()
メソッドによって影響を受けた要素の値を取得しようとした場合、どうすればよいでしょうか?
クリアされた要素の値にアクセスする
クリアされた要素の値を見つけて、C# コンパイラが null 値でどのように動作するかを確認するには、2 つの方法が必要です。
次のように、
Array.Clear(pallets, 0, 2);
コード行の周りに新しいコード行を挿入します。Console.WriteLine($"Before: {pallets[0]}"); Array.Clear(pallets, 0, 2); Console.WriteLine($"After: {pallets[0]}");
コードが次のコード リストと一致していることを確認します。
string[] pallets = { "B14", "A11", "B12", "A13" }; Console.WriteLine(""); Console.WriteLine($"Before: {pallets[0]}"); Array.Clear(pallets, 0, 2); Console.WriteLine($"After: {pallets[0]}"); Console.WriteLine($"Clearing 2 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); }
コード ファイルを保存してから、Visual Studio Code を使ってコードを実行します。
次の出力が表示されます。
Before: B14 After: Clearing 2 ... count: 4 -- -- -- B12 -- A13
出力 After:
の行に注目すると、pallets[0]
に格納されている値は空の文字列であると思うかもしれません。 しかし、C# コンパイラでは、null 値は表示用に空の文字列に暗黙的に変換されます。
クリアされた要素で文字列ヘルパー メソッドを呼び出す
クリアされた後で pallets[0]
に格納されている値が null であることを証明するため、pallets[0]
に対して ToLower()
メソッドを呼び出すようにコード例を変更します。 文字列の場合は、問題なく動作します。 しかし、null の場合は、コードによって例外がスローされます。
コンソールに
pallets[0]
を書き込むたびにToLower()
メソッドを呼び出すには、次のようにコードを更新します。Console.WriteLine($"Before: {pallets[0].ToLower()}"); Array.Clear(pallets, 0, 2); Console.WriteLine($"After: {pallets[0].ToLower()}");
あなたのコードが次のコード リストと一致することを確認してください。
string[] pallets = { "B14", "A11", "B12", "A13" }; Console.WriteLine(""); Console.WriteLine($"Before: {pallets[0].ToLower()}"); Array.Clear(pallets, 0, 2); Console.WriteLine($"After: {pallets[0].ToLower()}"); Console.WriteLine($"Clearing 2 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); }
コード ファイルを保存してから、Visual Studio Code を使ってコードを実行します。 今回は、コードを実行すると、大きなエラー メッセージが表示されます。 そのテキストを調べると、次のようなメッセージが表示されています。
System.NullReferenceException: Object reference not set to an instance of an object.
この例外がスローされるのは、C# コンパイラによって null が空の文字列に暗黙的に変換される前に、
pallets[0]
要素の内容に対してメソッドを呼び出そうとしたためです。この場合の教訓は、配列要素で値が参照されている場合は、
Array.Clear()
によってそれが削除されるということです。 これを解決するために、値を出力しようとする前に null を確認できます。エラーを回避するには、null である可能性のある配列要素にアクセスする前に、
if
ステートメントを追加します。
if (pallets[0] != null)
Console.WriteLine($"After: {pallets[0].ToLower()}");
配列のサイズを変更して要素をさらに追加する
次に、手順 1 のコード リストを修正して、配列のサイズを変更するためのコードを含めます。 完了すると、コードは次のコード リストと一致するはずです。
string[] pallets = { "B14", "A11", "B12", "A13" }; Console.WriteLine(""); Array.Clear(pallets, 0, 2); Console.WriteLine($"Clearing 2 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); } Console.WriteLine(""); Array.Resize(ref pallets, 6); Console.WriteLine($"Resizing 6 ... count: {pallets.Length}"); pallets[4] = "C01"; pallets[5] = "C02"; foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); }
少し時間取って、行
Array.Resize(ref pallets, 6);
に注目します。ここでは、
Resize()
メソッドを呼び出し、ref
キーワードを使用して参照でpallets
配列を渡しています。 場合によっては、メソッドに値渡し (既定) または参照渡し (ref キーワードを使用) で引数を渡す必要があります。 これが必要になる理由については、.NET でのオブジェクトの管理方法について、長くて複雑な説明が必要になります。 残念ながら、このモジュールの範囲を超えています。 疑わしい場合は、IntelliSense または Microsoft Docs で特定のメソッドを適切に呼び出す方法の例を調べることをお勧めします。この例では、
pallets
配列のサイズを 4 要素から6
に変更します。 新しい要素は、現在の要素の末尾に追加されます。 2 つの新しい要素は、値を代入するまでは null です。コード ファイルを保存してから、Visual Studio Code を使ってコードを実行します。 コードを実行すると、次の出力が表示されます。
Clearing 2 ... count: 4 -- -- -- B12 -- A13 Resizing 6 ... count: 6 -- -- -- B12 -- A13 -- C01 -- C02
配列のサイズを変更して要素を削除する
逆に、Array.Resize()
を使用して配列の要素を削除することもできます。
Visual Studio Code エディターで次のようにコードを更新します。
string[] pallets = { "B14", "A11", "B12", "A13" }; Console.WriteLine(""); Array.Clear(pallets, 0, 2); Console.WriteLine($"Clearing 2 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); } Console.WriteLine(""); Array.Resize(ref pallets, 6); Console.WriteLine($"Resizing 6 ... count: {pallets.Length}"); pallets[4] = "C01"; pallets[5] = "C02"; foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); } Console.WriteLine(""); Array.Resize(ref pallets, 3); Console.WriteLine($"Resizing 3 ... count: {pallets.Length}"); foreach (var pallet in pallets) { Console.WriteLine($"-- {pallet}"); }
コード ファイルを保存してから、Visual Studio Code を使ってコードを実行します。 コードを実行すると、次の出力が表示されます。
Clearing 2 ... count: 4 -- -- -- B12 -- A13 Resizing 6 ... count: 6 -- -- -- B12 -- A13 -- C01 -- C02 Resizing 3 ... count: 3 -- -- -- B12
Array.Resize()
の呼び出しでは、最初の 2 つの null 要素が削除されなかったことに注意してください。 むしろ、最後の 3 つの要素が削除されています。 注目すべきは、文字列値が含まれていたにもかかわらず、最後の 3 つの要素が削除されたことです。
配列から null 要素を削除できるか
Array.Resize()
メソッドによって配列から空の要素が削除されないとしたら、ジョブが自動的に行われる別のヘルパー メソッドがあるのでしょうか。 いいえ。 配列から空の要素を削除する最善の方法は、各項目を反復処理して変数 (カウンター) をインクリメントし、null 以外の要素の数をカウントすることです。 次に、カウンター変数のサイズで 2 番目の配列を作成します。 最後に、元の配列内の各要素をループし、null 以外の値を新しい配列にコピーします。
要点
このユニットで説明したいくつかの重要なアイデアを以下に示します。
- 配列内の要素の値を空にするには、
Clear()
メソッドを使用します。 - 配列内の要素の数を変更し、配列の末尾の要素を削除または追加するには、
Resize()
メソッドを使用します。 - 新しい配列要素とクリアされた要素は null になります。つまり、メモリ内の値はポイントされていません。