【★ワレコのC#講座】配列ArrayとリストList<T>をマスターする【便利】

この記事は約35分で読めます。
スポンサーリンク

Visual Basic、EXCEL VBAあるいはVB.NETなどのBasic系の言語は使えるけれど、C、C++、C#などC言語系は何となく知っているが使った事がない、そんな人は多いだろう。

ワテの場合も正に(まさに)そんな感じ。

その後、必死に猛勉強してC、C++、C#などを完璧にマスターした(と言う事にしておくが)今となっては、C言語系は、とっても使い易くて気に入っている。

しかしながら、Basic系しか知らない人にとってはC言語系の文法は難解で分り辛い。

そんなあなたの為に、今回はC#の配列とリストを紹介しよう。

配列と言う概念はBasic系言語に限らず、殆ど全ての言語が持っている。

一方、リスト機能は最近の言語ならほぼ使えるとは思うが、大昔にFORTRANやBASICなどでプログラミングを勉強した人には馴染みの無い概念だ。

この記事では、そのリストをマスターする事を目的としている。

 

なお、プログラミングの世界で「リスト」と言うと、所謂(いわゆる)C言語などでポインタを使って互いに要素を連結するリスト構造を意味する場合もあるが、ここで言うリストとは、配列の機能を便利に拡張したやつだ。

そのリストの機能を実装する為には、内部的にはリスト構造を利用するなどしていると思うので、そう言う意味でリストと名前が付いているのかな?完璧にマスターしているにも拘わらずその辺りは疎い(うとい)ワテである。でもまあ多分そうだと思う。

リストを覚えると、従来式の配列は使いたくなくなるくらいにリストは便利なのだ。

では、さっそくリストを覚えよう。

リストを聴きながらリストを勉強する。

有名な人らしいがこの人が誰なのかはワテは知らない。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
スポンサーリンク
スポンサーリンク

まずはC#の配列を覚える

文字列型の配列を作成する。

要素数は3個で、三つの文字列を代入する。

そしてコンソールウインドウに中身を表示すると言う簡単なC#プログラムだ。

サンプル1:三つの要素を持つ配列を作成し、値を代入

Console.WriteLine("サンプル1:三つの要素を持つ配列を作成し、値を代入する。");
string[] strArr = new string[3];
strArr[0] = "要素0";
strArr[1] = "要素1";
strArr[2] = "要素2";
int lenArr = strArr.Length;
Console.WriteLine("strArrの要素数 = {0}", lenArr);
for (int i = 0; i < strArr.Length; i++)
{
    Console.WriteLine("strArr[{0,3:d}] = ->{1}<-", i, strArr[i]);
}     

その実行結果は以下の通り。

サンプル1:三つの要素を持つ配列を作成し、値を代入する。
strArrの要素数 = 3
strArr[ 0] = ->要素0<-
strArr[ 1] = ->要素1<-
strArr[ 2] = ->要素2<-
続行するには何かキーを押してください . . .

もし上のサンプルプログラムでConsole.WriteLine()の書式設定の部分が気になった人はこの記事がお勧めだ。

【ワレコC#】Console.WriteLine()書式指定で右詰め・ゼロ埋め・桁指定・16進など
ワテの場合、C# を使い始めてからかなりの年月が経つのだが、Console.WriteLine() の書式指定が未だに覚えられない。Console.WriteLine() とは、C#のコンソールアプリケーション、つまり実行するとコンソール画

右詰、左詰め、ゼロ埋め、16進表示などなど、ワテも良く忘れるのでこの記事でまとめておいた。

サンプル1の説明

文字列型の配列の場合、以下のように大きさを指定して宣言すれば良い。

string[] strArr = new string[3];

これで、要素三つ(0, 1, 2)の配列が宣言出来る。

こう言う記述に慣れていない人が見ると、左辺の string[] はカッコ[]の中が空なので気持ち悪いと思うかも知れない。

string[] の意味は、string型の配列と言う意味。つまり [] が配列を意味していると思えば良いだろう。

一方、右辺では new string[3] のように要素数3を指定して記述する。

newの概念に慣れていない人は、ここでも違和感を覚えるかも知れないが、C#では兎に角newを付けて配列宣言をすると覚えておけば良いだろう。

ちなみにC++言語ではnewで確保したメモリ領域は、使い終わったら必ず自分でdeleteして解放する必要がある。ところが、C#の場合には使う時にはnewするが、あとはdeleteも何もしなくても、C#あるいは.NET Frameworkの実行環境が上手い具合にメモリ管理をしてくれるのだ。

とっても楽ちん。

string型配列の初期値は空文字列ではなくてnullになる

サンプル1のように配列を宣言した場合には、初期値はnullが入る(下表)。

配列インデックス 配列データ
0 null
1 null
2 null

文字列型配列の初期値は空文字列 “” では無いので要注意だ。

C#のstring型はnull許容型、つまり文字列だけでなくnullという特殊な値を代入出来る。

まあ、nullとはまだ初期値が与えられていない状態と言う感じだ。

 

参考までに言うと、こんなif文を書けば文字列がnullあるいは空文字列の場合を検出できる。

この例の文字列はstrArrの0番目の要素をたまたま用いているが、どんな文字列でも良い。

if (strArr[0] == null || strArr[0]== ""){
    strArr[0] = "初期値を与える";
}

//でも、以下に示す便利な判定方法を使うと上の判定と同じになる。
if (string.IsNullOrEmpty(strArr[0]))
{
    strArr[0] = "初期値を与える";
}
//あるいは null、空文字列に加えて空白やタブや改行などを検出するなら 
if (string.IsNullOrWhiteSpace(strArr[0]))
{
    strArr[0] = "初期値を与える";
}
//と書ける

まあ配列とは関係ない話題だが、C#で文字列を扱う場合にstring.IsNullOrEmpty()などをよく使うので覚えておくと良いだろう。

以上が、C#の配列の基本だ。

Stringとstringは同じ物(ワテの理解では)

さて、少し余談になるが、C#関連サイトを見ていると全部小文字のstringと先頭大文字のStringが登場する。

string[] strArr = new string[3];
String[] StrArr = new String[3];

気になる人は両者の違いを調べてみると良いが、ワテの理解では、同じ物だ。

なので、好きな方を使えば良いし、当記事のサンプルプログラムをStringに書き換えても問題なく実行出来る。

Stringstringが混在していても良いが、混乱の元なのであまりお勧めはしないが。

ワテの場合には小文字版 string を使う。

理由は、キー入力する時に先頭を大文字にする必要が無いので入力が楽だからだ。

 

次に、整数型配列、倍精度浮動小数点数型配列ならこんな感じ。

int   [] intArr = new int[3];
double[] dblArr = new double[3];

 

varを使って左辺を簡潔に記述出来る

あるいは左辺の先頭はvarを使っても良い。

var strArr  = new string[3];
var StrArr  = new String[3];
var intArr  = new int[3];
var dblArr  = new double[3];

このようにvar宣言すると、右辺でnew作成している型を使って左辺の配列変数の型が自動で決まるのだ。

varは、Visual C# 3.0 以降で使えるようになった機能であり、変数に暗黙的な “型” var を与えることができるのだ。

まあ、ワテの場合はvarを使ったり使わなかったり、一貫性は無い。

 

さて、配列操作の二番目のサンプルを見てみよう。

サンプル2:初期値を与えて配列を宣言する

配列宣言時に初期値を与えたいと言う状況も良くある。

それをやるにはこんな風にすれば良い。

 Console.WriteLine("サンプル2:三つの要素を持つ配列を初期値を与えて作成する。");
 string[] strArr = new string[] { "要素0", "要素1", "要素2" };
 int lenArr = strArr.Length;
 Console.WriteLine("strArrの要素数 = {0}", lenArr);
 for (int i = 0; i < strArr.Length; i++)
 {
     Console.WriteLine("strArr[{0,3:d}] = ->{1}<-", i, strArr[i]);
 }  

要するにstring[]{…}の中で初期値を指定する訳だ。

 

まあ、言うまでも無くこんな風に、複数行に渡って書いても良い。

 string[] strArr = new string[] {
     "要素0",
     "要素1",
     "要素2"
 };

 

さて、次の例は配列のサイズを増やしたい場合のテクニックだ。

最初に宣言した配列要素の要素数を後で変更する場合には、現時点で配列に保管されているデータがどうなるのか気になる。

C#に限らず、他の言語でも同様の問題が起こる。

サンプル3:配列要素に新しい要素を追加したい

まず、現時点で下表のように初期値がセットされているとする。

配列インデックス 配列データ
0 “要素0”
1 “要素1”
2 “要素2”

その内容を保持したまま、末尾に新しい要素を一個追加するのが目的だ。

配列インデックス 配列データ
0 “要素0”
1 “要素1”
2 “要素2”
3 “要素3”

VB.NETなら簡単なのだが、C#ではそうは問屋が卸さない。

VB.NETならReDim Preserveが使える

VB.NETなら配列要素数を変更する場合には、ReDim Preserveと言う便利なコマンドがある。

単にReDimだと値が失われるが、Preserveを付けると文字通り値を保持したまま配列の大きさを変えられる。

そのVB.NETコードの例を下に示す。

Module Module1
 
    Sub Main()
 
        Dim strArr = New String() {"0", "1", "2"}
 
        ReDim Preserve strArr(5)
        strArr(3) = "3"
        strArr(4) = "4"
 
        For Each item In strArr
            Console.WriteLine("{0} ", item)
        Next
 
    End Sub
 
End Module

VB.NETならこんなに簡単に配列要素数を増やす事が可能だ。

その実行結果は以下の通り。

0
1
2
3
4
続行するには何かキーを押してください . . .

でも、C#にはReserveが無い。

その為にC#で既存の配列の大きさを変更する場合にはテクニックと言うか、小細工と言うか、ありきたりの方法と言うか、まあそんなのが必要になる。

そのサンプルC#プログラムを以下に示す。

サンプル3:配列末尾に要素を一つ追加する小細工

Console.WriteLine("サンプル3:配列末尾に要素を一つ追加する小細工");
var strArr = new string[] { "要素0", "要素1", "要素2" };
 
var tmpArr = new string[strArr.Length]; // まず現在の値を一時保管するために同サイズの仮の配列作成
strArr.CopyTo(tmpArr, 0);               // 現在の値を仮配列にコピー
strArr = new string[strArr.Length + 1]; // 現在の配列のサイズを一つ増やす。中身は消える。
tmpArr.CopyTo(strArr, 0);               // 仮配列から現在の配列に値を戻す。
strArr[strArr.Length - 1] = "要素3";   // 現在の配列の末尾に目的の要素を追加する。
 
var lenArr = strArr.Length;
Console.WriteLine("strArrの要素数 = {0}", lenArr);
for (var i = 0; i < strArr.Length; i++)
{
    Console.WriteLine("strArr[{0,3:d}] = ->{1}<-", i, strArr[i]);
}

その実行結果。

サンプル3:配列末尾に要素を一つ追加する小細工
strArrの要素数 = 4
strArr[ 0] = ->要素0<-
strArr[ 1] = ->要素1<-
strArr[ 2] = ->要素2<-
strArr[ 3] = ->要素3<-
続行するには何かキーを押してください . . .

 処理内容は上のサンプルコードを見れば大体分ると思うが念のために説明しておこう。

サンプル3の説明

仮配列を作成しておいて現在の値を保管しておく。

var strArr = new string[] { "要素0", "要素1", "要素2" };
 
var tmpArr = new string[strArr.Length]; // まず現在の値を一時保管するために同サイズの仮の配列作成
strArr.CopyTo(tmpArr, 0);               // 現在の値を仮配列にコピー

 

そして、配列の大きさを変更し、保管していた値を戻す。

strArr = new string[strArr.Length + 1]; // 現在の配列のサイズを一つ増やす。中身は消える。
tmpArr.CopyTo(strArr, 0);               // 仮配列から現在の配列に値を戻す。
strArr[strArr.Length - 1] = "要素3";   // 現在の配列の末尾に目的の要素を追加する。

無事に配列の大きさを一つ増やす事が出来たので、末尾要素に目的の初期値をセットする。

これで完了だ。

まあ、C#の配列の場合 CopyTo()と言うメソッドが使えるのでその点は便利だ。

もしCopyTo()を使わないで同じ事をするなら、forループで値をコピーする処理を書けば良い。

C#の配列のまとめ

と言う事でC#の配列をまとめるならば、配列の要素数を途中で変更する時には現在の値を維持したまま変更するのが面倒くさい。

また、今回は実例を示さなかったが、配列要素の途中に新要素を追加するとなると、益々ややこしい小細工処理が必要だ。

あるいは、要素を削除する場合も似たような小細工処理が必要となる。

そう言うC#配列の面倒な部分を一気に解決してくれるのが今から紹介するリストなのだ!

C#のリストをマスターする

今まで紹介した配列版のサンプルをList版に書き換えた。

 Console.WriteLine("サンプル4:リストの基本形");
 var strList = new List<string>() { "要素0", "要素1", "要素2" };
 var countList = strList.Count;
 Console.WriteLine("strListの要素数 = {0}", countList);
 for (var i = 0; i < strList.Count; i++)
 {
     Console.WriteLine("strList[{0,3:d}] = ->{1}<-", i, strList[i]);
 }

この例では、要素数3のリストを初期値を与えて作成している。

そしてその実行結果は以下の通り。

サンプル4:リストの基本形
strListの要素数 = 3
strList[ 0] = ->要素0<-
strList[ 1] = ->要素1<-
strList[ 2] = ->要素2<-
続行するには何かキーを押してください . . .

三個の要素が正しくセットされている事が分った。

さて、今回初めて登場したリストであるが、new List<string>() {…} と言う書き方を初めて見ると戸惑う人も多い。

何で不等号を使うの?と言った疑問も湧くだろう。

まあ、あまり気にしない。

配列なら string[] strArr のように角カッコ、

リストなら List<string>の様に不等号記号、まあ慣れれば問題無い。

リストのリストも可能、何を入れても良い

ちなみに、リストの中には何を入れても良い。stringとかintと言った単純な型でも良いし、List<int>をListの中に入れても良い。あるいは string[] をListに入れてみた。

var strList = new List<string>() { "要素0", "要素1", "要素2" };
var intList = new List<int>() { 0, 1, 2 };
var intListList = new List<List<int>>() {
    new List<int>{  0, 01, 02 },
    new List<int>{ 10, 11, 12 },
    new List<int>{ 20, 21, 22 },
};
var strArrList = new List<string[]>() {
    new string[]{ " 0", "01", "02" },
    new string[]{ "10", "11", "12" },
    new string[]{ "20", "21", "22" },
};

さて本題に戻ると、

リストの末尾に一つ要素を追加する

このように作成したリストの末尾に一つ要素を追加するには以下の一行を実行するだけで良い。

strList.Add("要素4");

 

なんやて?

ワテの場合、こう言う状況ではこの映画の55秒あたりのシーンを思い出す。

極道の妻たち 最後の戦い(予告編)

まさに「なんやて!?」

配列でやったような一時保管用の仮変数を作成する必要もない。

Add()を実行するだけでC#さんが裏で上手い具合にメモリ管理などをやってくれているのだ。

 

リスト末尾に要素を一個追加する全ソースコードを紹介しよう。

 Console.WriteLine("サンプル4:リストの基本形");
 var strList = new List<string>() { "要素0", "要素1", "要素2" };
 var countList = strList.Count;
 Console.WriteLine("strListの要素数 = {0}", countList);
 for (var i = 0; i < strList.Count; i++)
 {
     Console.WriteLine("strList[{0,3:d}] = ->{1}<-", i, strList[i]);
 }
 
 Console.WriteLine("要素を末尾に一個追加");
 strList.Add("要素3");
 for (var i = 0; i < strList.Count; i++)
 {
     Console.WriteLine("strList[{0,3:d}] = ->{1}<-", i, strList[i]);
 }

上のコードではforループで変数の中身を出力する部分があるので見た目がややこしいが、肝心なのは  strList.Add(“要素3”); の部分のみだ。

この一行で要素をリスト末尾に追加出来るのだ。

その実行結果は以下の通り。

サンプル4:リストの基本形
strListの要素数 = 3
strList[ 0] = ->要素0<-
strList[ 1] = ->要素1<-
strList[ 2] = ->要素2<-
要素を末尾に一個追加
strList[ 0] = ->要素0<-
strList[ 1] = ->要素1<-
strList[ 2] = ->要素2<-
strList[ 3] = ->要素3<-
続行するには何かキーを押してください . . .

どんなもんじゃ焼き!

じゃなくて、どんなもんじゃい!

これがワテの実力だ!じゃなくて、これがリストの実力だ!!

正に「目から鱗が落ちる」とはこの事か。

DIYマニアな人はここでどんなもんじゃいノコギリを買うと良いだろう。

いや、もんじゃ焼きが食べたくなった人は、どんなもんじゃ!?がお勧めだ。

そんなもんじゃを売っているのか!!

もう訳分からんので次に進もう。

リストの任意の要素を一個削除する

追加方法は分ったので、要素の削除方法を紹介しよう。

{
    Console.WriteLine("サンプル4:リストの基本形");
    var strList = new List<string>() { "要素0", "要素1", "要素2" };
    var countList = strList.Count;
    Console.WriteLine("strListの要素数 = {0}", countList);
    dumpList(strList);
 
    Console.WriteLine("要素を末尾に一個追加");
    strList.Add("要素3");
    dumpList(strList);
 
    Console.WriteLine("二番目要素を削除");
    strList.RemoveAt(1);
    dumpList(strList);
}
 
void dumpList(List<string> strList)
{
    for (var i = 0; i < strList.Count; i++)
    {
        Console.WriteLine("strList[{0,3:d}] = ->{1}<-", i, strList[i]);
    }
}

変数の内容を出力する部分を関数化してスッキリ記述した。

リスト要素の削除も簡単で、RemoveAt(1) のように目的の要素のインデックスを指定して削除するだけで良い。 

 

その実行結果は以下の通り。

サンプル4:リストの基本形
strListの要素数 = 3
strList[ 0] = ->要素0<-
strList[ 1] = ->要素1<-
strList[ 2] = ->要素2<-
要素を末尾に一個追加
strList[ 0] = ->要素0<-
strList[ 1] = ->要素1<-
strList[ 2] = ->要素2<-
strList[ 3] = ->要素3<-
二番目要素を削除
strList[ 0] = ->要素0<-
strList[ 1] = ->要素2<-
strList[ 2] = ->要素3<-
続行するには何かキーを押してください . . .

上に示すように、インデックス 1番の要素が元々インデックス 2 に有った要素に変わっている。

以下、同様に全部の要素が一つ上にずれている。完璧や!

 

二つのリストをマージする(連結する)

リストを使うと複数のリストを連結して新しいリストを作成する操作も簡単に出来る。

{
    Console.WriteLine("サンプル5:二つのリストをマージする例");
    var strList1 = new List<string>() { "要素0", "要素1", "要素2" };
    var strList2 = new List<string>() { "要素3", "要素4", "要素5", "要素6" };
    dumpList("strList1", strList1);
    dumpList("strList2", strList2);
 
    Console.WriteLine("二つのリストをマージした");
    strList1.AddRange(strList2);      // これでリストのマージが出来る
    dumpList("strList1", strList1);
    dumpList("strList2", strList2);
}
 
void dumpList(string nameList, List<string> strList)
{
    Console.WriteLine("リスト {0} の中身", nameList);
 
    for (var i = 0; i < strList.Count; i++)
    {
        Console.WriteLine("{0}[{1,3:d}] = ->{2}<-", nameList, i, strList[i]);
    }
}

リストのマージにはAddRangeを使えば良い。

これでstrList1の末尾にstrList2を簡単に連結出来る。

strList2の中身は変化しない。

その実行結果は以下の通り。

サンプル5:二つのリストをマージする例
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-
リスト strList2 の中身
strList2[ 0] = ->要素3<-
strList2[ 1] = ->要素4<-
strList2[ 2] = ->要素5<-
strList2[ 3] = ->要素6<-
二つのリストをマージした
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-
strList1[ 3] = ->要素3<-
strList1[ 4] = ->要素4<-
strList1[ 5] = ->要素5<-
strList1[ 6] = ->要素6<-
リスト strList2 の中身
strList2[ 0] = ->要素3<-
strList2[ 1] = ->要素4<-
strList2[ 2] = ->要素5<-
strList2[ 3] = ->要素6<-
続行するには何かキーを押してください . . .

上に示すように、要素3個と4個の二つのリストを連結して、要素7個のリストを作成出来た。

ほんなら今回の学習の最後に、リストをコピーする方法も覚えよう。

C#でリストのコピーの間違ったやり方

ワテの場合、リストを覚え立ての頃にこんな失敗をした。

{
    Console.WriteLine("サンプル6:リストをコピーする間違い例");
    var strList1 = new List<string>() { "要素0", "要素1", "要素2" };
    dumpList("strList1", strList1);
 
    Console.WriteLine("リストをコピーする間違いの例");
    var strList2 = strList1;        // リストをコピーしたつもり
    dumpList("strList1", strList1);
    dumpList("strList2", strList2);
 
    Console.WriteLine("リストの中身を変更してみる");
    strList1[0] = "なんでやねん!"; // strList1を変更したのだが、
    dumpList("strList1", strList1);
    dumpList("strList2", strList2);
    Console.WriteLine("何故か両方のリストの中身が変更されている!?");
}
 
void dumpList(string nameList, List<string> strList)
{
    Console.WriteLine("リスト {0} の中身", nameList);
 
    for (var i = 0; i < strList.Count; i++)
    {
        Console.WriteLine("{0}[{1,3:d}] = ->{2}<-", nameList, i, strList[i]);
    }
    Console.WriteLine();
}

上のコードで、 var strList2 = strList1;  の部分があるが、このように代入すれば strList1と全く同じ中身を持つ新しいリスト strList2 が作成出来ると思ったのだ。

実際にやってみると、デバッガーで調べても中身はコピー出来ていて strList2の中身はstrList1と同じだ。

ところが、その後でstrList1の中身を変えると何故か strList2の中身も変わる。

それも同じ値に変わる。

狐につままれた気分だ。

その実行結果。

サンプル6:リストをコピーする間違い例
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リストをコピーする間違いの例
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リスト strList2 の中身
strList2[ 0] = ->要素0<-
strList2[ 1] = ->要素1<-
strList2[ 2] = ->要素2<-

リストの中身を変更してみる
リスト strList1 の中身
strList1[ 0] = ->なんでやねん!<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リスト strList2 の中身
strList2[ 0] = ->なんでやねん!<-
strList2[ 1] = ->要素1<-
strList2[ 2] = ->要素2<-

何故か両方のリストの中身が変更されている!?
続行するには何かキーを押してください . . .

このように、strList2の中身はstrList1に連動して変わってしまう。

 

おかしい?

C#リストのコピーの正しいやり方

//x   var strList2 = strList1;                    // リストをコピーしたつもり[間違い]
      var strList2 = new List<string>(strList1);  // リストをコピーする正しいやり方

C#におけるリストのコピーは、上のように new を使って作成する必要がある。

このように記述すると、List<string>(strList1) の丸カッコの中に指定している strList1 を使って新しいstrList2を作成すると言う意味だ。

まあ要するにList<string>のコンストラクタの引数に strList1 を与えて実行したと言う事だ。

実際に試してみよう。

正しいリストコピー方法を使った全ソースコードは以下の通り

C#のリストコピーの正しいやり方 – 全コード

  {
      Console.WriteLine("サンプル7:リストをコピーする正しい例");
      var strList1 = new List<string>() { "要素0", "要素1", "要素2" };
      dumpList("strList1", strList1);
 
      Console.WriteLine("リストをコピーする正しい例");
//x   var strList2 = strList1;                    // リストをコピーしたつもり[間違い]
      var strList2 = new List<string>(strList1);  // リストをコピーする正しいやり方
      dumpList("strList1", strList1);
      dumpList("strList2", strList2);
 
      Console.WriteLine("リストの中身を変更してみる");
      strList1[0] = "こっちだけ変わるので正常";   // strList1のみ変更される。
      dumpList("strList1", strList1);
      dumpList("strList2", strList2);
      Console.WriteLine("無事に目的のリストの中身のみ変更出来た。");
  }
 
  void dumpList(string nameList, List<string> strList)
  {
      Console.WriteLine("リスト {0} の中身", nameList);
 
      for (var i = 0; i < strList.Count; i++)
      {
          Console.WriteLine("{0}[{1,3:d}] = ->{2}<-", nameList, i, strList[i]);
      }
      Console.WriteLine();
  }

その実行結果は以下の通り。

サンプル7:リストをコピーする正しい例
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リストをコピーする正しい例
リスト strList1 の中身
strList1[ 0] = ->要素0<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リスト strList2 の中身
strList2[ 0] = ->要素0<-
strList2[ 1] = ->要素1<-
strList2[ 2] = ->要素2<-

リストの中身を変更してみる
リスト strList1 の中身
strList1[ 0] = ->こっちだけ変わるので正常<-
strList1[ 1] = ->要素1<-
strList1[ 2] = ->要素2<-

リスト strList2 の中身
strList2[ 0] = ->要素0<-
strList2[ 1] = ->要素1<-
strList2[ 2] = ->要素2<-

無事に目的のリストの中身のみ変更出来た。
続行するには何かキーを押してください . . .

上に示すように、コピーした後で元データのstrList1の要素を変更しても、複製で作成したstrList2は影響を受けていない。

コピー成功や!

値型と参照型の違い

今回の問題は何が原因かと言うと、C#には値型と参照型とポインター型と言う三つの変数型がある。

それらに関する詳しい説明は下のマイクロソフト社の公式サイトで調べて下さい。

型 (C# リファレンス)

C# Keywords

このサイトから引用するとC#の値型と参照型は以下の通り。

値型 参照型

bool
byte
char
decimal
double
enum
float
int

long
sbyte
short
struct
uint
ulong
ushort

class
delegate
dynamic
interface
object
string
補間文字列

表 C#の値型と参照型一覧

まあ、ワテの理解では値型は普通の変数。

参照型は実体が一個あり、参照型変数はその実体へのポインタみたいな感じ。

でもまあ、ワテの理解よりもマイクロソフト社の公式な説明を読むほうが良いだろう。

参照型 (C# リファレンス)
共同作成者 Bill Wagner OpenLocalizationService Saisang Cai
C# では、参照型と値型という 2 種類の型をサポートしています。

参照型の変数はデータ (オブジェクト) への参照を格納するのに対して、値型の変数はデータを直接格納します。

参照型の場合、2 つの変数が同じオブジェクトを参照できるため、ある変数に対する演算によって、他の変数が参照しているオブジェクトが影響を受ける可能性があります。

値型の場合、各変数が独自のデータ コピーを保持し、ある変数に対する操作が別の変数に影響を与えることはありません (ref パラメーターと out パラメーターの変数を除きます。「ref」と「out パラメーター修飾子」を参照してください)。

引用元 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/types

配列やリストを相互に変換する

当記事で紹介したようにリストは使い易いが配列は旧態依然(きゅうたいいぜん)としていて使い辛い。

そんなあなたにお勧めな方法を紹介しよう

 var strList = strArr.ToList<string>();  // 配列からリストへ変換1
 var strList = strArr.ToList();     // 配列からリストへ変換2(これで十分)
 
 var strArr = strList.ToArray<string>(); // リストから配列へ変換1
 var strArr = strList.ToArray();         // リストから配列へ変換2(これで十分)

このように ToList() や ToArray() のメソッドを使えば、配列とリストを相互に変換可能なのだ。

なので、例えば配列で作業していて、その末尾に要素を一個追加したい場合には、配列をToList()でリスト化し、要素を追加。そしてToArray()で配列に戻すなどの泥臭い小細工も可能だ。

実際にやってみる。

var tmpList = strArr.ToList();
tmpList.Add("要素をリスト末尾に追加");
strArr = tmpList.ToArray();

こんな感じで配列末尾に要素を一個追加出来る。

この手法は便利ではあるが、ToList() や ToArray() を行うので効率は良くない。速度を求められる処理では、こんな変換を多用する手法はお勧めでは無い。なので、最初からリストのみを使う設計にしておくのが良い。

なお、ToArray<string>() と明示的に変換すべきstring型を指定しても良いが、今の場合にはList<string>からArrayへの変換なので、何も指示しなくてもstring型のArrayだと分かる。

そう言う場合には Array() のように<> の部分を省略できる。

では、どんな時に明示的に型を指定する必要があるかと言うと、今は良い例を思い付かないので、それは別の機会に説明したい。

 

以上で今回のC#講座を終わりたい。

以下、補足。

ArrayListについて

C#で配列やリストに付いて調べていると、ArrayListと言うのを目にすると思う。

ArrayListの機能を使うと今回の講座でListでやったのと似たような事が出来る。

しかしながら、ArrayListは .NET Framework 1.1 以降で使用可能になった古い技術なのだ。

最近のC#はバージョンもC# 7.1と大きく進化していて、.NET Framework 4.7が最新だ。

その最近のC#でリストの機能を使う場合は、ArrayListではなくてここで紹介したListの機能が推奨されている。なので、ArrayListは使わない方がよい。

まあArrayListとListの違いなどが気になる人はマイクロソフト社の公式サイトを見て勉強すると良いだろう。

ArrayList Class (System.Collections)
必要に応じてサイズが動的に拡大される配列を使用して インターフェイスを実装します。Implements the interface using an array whose size is dynamically increased as required.

まとめ

この記事ではC#に於ける配列、リストの違いを学ぶ事が出来た。

リストを使うと要素の追加、削除が簡単に出来る。

同じくリストを使うと、複数のリストの連結処理が簡単に出来る。

値型、参照型の違いに注意する必要がある。

リストなどのオブジェクト型の変数の場合には参照型なので、変数を単純に代入する方式ではコピーは作成出来ない。

リスト変数を単純代入しても、リストの実体はそのままで、リスト実体への参照がコピーされただけだ。

リストのコピーを正しく行うには、リストのコンストラクタにコピー元変数を入れて newで新規のリストを作成する。

ArrayListは .NET Framework の古い手法なので、今は使わない方がよい。

本で勉強する

まあ、楽しく勉強したい人はこんな本もお勧めだ。

 

超入門とは一体全体どんな入門なのか気になる。

 

ワテの場合、イデオムと言う単語の意味が分からん。でもアマゾンでは人気があるみたいだ。

 

この独習C#は有名だ。

アマゾンレビューの評価も高いのでお勧めだ。ただしワテは読んだことは無い。

 

やっぱり、こう言うポケットリファレンスを鞄に一冊だけしのばせておいて、時々気になった事が有れば辞書代わりに使う、そう言う使い方をしているとあなたも上級者っぽく見えるだろう。

  • 単行本(ソフトカバー): 512ページ
  • 発売日: 2017/6/20
  • 梱包サイズ: 18.8 x 13 x 2.2 cm

なのでコンパクトだが512ページもあるのか!

かなり安値で本を買う(本以外も買えます)

2017年のゴールデンウイークを利用して便利なWEBサイトを作ってみた。

名付けて、

最安価格サーチ

クリックで開く

初回起動時のみ3秒くらい掛かりますがご了承下さい。

 

何が出来るかと言うと、

Amazon.co.jp

楽天市場

ヤフーショッピング

の三つのショッピングサイトを同時検索して、商品を価格の安い順に表示出来るお買い物支援サイトだ。

「最安価格サーチ」で、

「Androidプログラミング」をかなり安値で探したい人は こちらから >

「Xamarinプログラミング」をかなり安値で探したい人は こちらから >

「C#プログラミング」をかなり安値で探したい人は こちらから >

「Visual Studioプログラミング」をかなり安値で探したい人は こちらから >

 

もしお使い頂きまして何かご不明な点、改善案などありましたらお知らせ下さい。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

C#
スポンサーリンク
warekoをフォローする
スポンサーリンク
われこ われこ

コメント