■概要
C#でのListを使用する際、int型やstring型はよく使用するが、「クラス(オブジェクト)型」を使用するとどのような挙動になるのかを確認する。
■int型のリストについて
namespace ListCheck
{
internal class Program
{
static void Main(string[] args)
{
//===============================
//int型のList
//===============================
List<int> a = new List<int>();
a.Add(1);
a.Add(2);
a.Add(3);
for(int i = 0; i < a.Count; i++)
{
Console.WriteLine(a[i]);
}
}
}
}
1
2
3
よく見る通常のリストの型。
出力も追加された順にそのまま出力が行われる。
■クラス(オブジェクト)型のリストについて
namespace ListCheck
{
internal class Program
{
static void Main(string[] args)
{
//===============================
//オブジェクト型のList
//===============================
List<Info> b = new List<Info>();
//「オブジェクト初期化子」でデータを追加
b.Add(new Info { Age = 20, Name = "佐藤" });
b.Add(new Info { Age = 30, Name = "鈴木" });
b.Add(new Info { Age = 40, Name = "田中" });
b.Add(new Info { Age = 50 });
b.Add(new Info { Age = 60 });
b.Add(new Info { Name = "渡辺" });
b.Add(new Info { Age = 70 });
b.Add(new Info { Name = "高橋" });
for (int i = 0; i < b.Count; i++)
{
Console.Write(i + ".)"); //Loopの値を表示
Console.Write(b[i].Age + "歳"); //年齢を表示
Console.WriteLine(":" + b[i].Name); //名前を表示
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ListCheck
{
internal class Info
{
public int Age { get; set; }
public string Name { get; set; } = string.Empty;
}
}
0.)20歳:佐藤
1.)30歳:鈴木
2.)40歳:田中
3.)50歳:
4.)60歳:
5.)0歳:渡辺
6.)70歳:
7.)0歳:高橋
i | Age | Name |
---|---|---|
0 | 20 | 佐藤 |
1 | 30 | 鈴木 |
2 | 40 | 田中 |
3 | 50 | 空欄 |
4 | 60 | 空欄 |
5 | 0 |
渡辺 |
6 | 70 | 空欄 |
7 | 0 |
高橋 |
今回の例だと、List<Info> b = new List<Info>();
でInfoクラス型のListになる。
①クラス(オブジェクト)型の場合の代入方法について
b.Add(new Info { Age = 20, Name = "佐藤" });
この型のListの場合、「オブジェクト初期化子」を使用してデータを追加する。
以下引用
オブジェクト初期化子を使用すると、オブジェクトの作成時にアクセスできるフィールドまたはプロパティに、コンストラクターを呼び出して代入ステートメントを使用しなくても、値を割り当てることができます。
このオブジェクト初期化子について簡単に説明をすると、「インスタンスの生成+プロパティの初期化(値のセット)」を同時に行える便利な記載方法。
≒ インスタンスの生成とプロパティへの値の追加を1行で行える。
・オブジェクト初期化子の基本的な使い方の一例
Info person = new Info { Age = 20, Name = "佐藤" };
オブジェクト初期化子の一例は上記。
personという変数にオブジェクト初期化子の値を入れる。
こう見るとインスタンスの生成+プロパティの初期化(値のセット)を同時に行っていることがわかる。
・今回の記載方法
b.Add(new Info { Age = 20, Name = "佐藤" });
今回の場合は上記の書き方。
Info person...の場合はリスト追加時「b.Add(person)」とする必要がある。
つまり、今回の記載方法の場合はAddの引数を直接オブジェクト初期化子とすることで変数に代入する手間を省いている。
また、その他の方法として以下の2つの代入方法も考えられる。
方法①:普通にインスタンスを作成してから追加
//インスタンスの生成
Info person = new Info();
person.Age = 20;
person.Name = "佐藤";
b.Add(person);
もし10行追加するとなった場合、この3行を10セット分(=30行分)記載する必要がある。
方法②:コンストラクタを使う
class Info
{
public int Age { get; set; }
public string Name { get; set; }
// コンストラクタを作成
public Info(int age, string name)
{
Age = age;
Name = name;
}
}
// コンストラクタを使ってデータ追加
b.Add(new Info(20, "佐藤"));
このように見ると、コンストラクタの場合とオブジェクト初期化子の場合ではさほど記載量の違いはないように見える。
しかし、違いとしては以下があげられる。
比較項目 | オブジェクト初期化子 | コンストラクタ |
---|---|---|
コードの簡潔さ | 〇 | △ |
バリデーション | × できない | 〇 できる |
読み取り専用プロパティ (private set;) | × 使えない | 〇 |
既存のクラスに適用 | 〇 (コンストラクタの明示的な記載がない場合) | △ (コンストラクタの追加記載必要) |
一部のプロパティにのみ追加 | 〇 | × すべて設定が必須 |
リスト追加時の使いやすさ | 〇 | △ 書く量が少し増える |
つまり、下記の場合はコンストラクタを使う必要がある。
バリデーションが必要
private set; を使う
必ず値をセットする必要がある
②一部のプロパティのみ初期化した場合の値について
AgeとNameをセットで代入しなかった行は初期値に「0」もしくは「空欄」が入っている。
b.Add(new Info { Age = 60 });
b.Add(new Info { Name = "渡辺" });
例えば上記の場合、AgeとNameのセットで代入されてはいないが同じリスト内に収まりそうな気はする。
しかし、実際はInfoクラスのプロパティは2つ。
「new Info { Age = 60 }」 と「 new Info { Name = "渡辺" } 」はそれぞれ異なるインスタンス(別のオブジェクト)として作成され,、リストに追加される。
つまり、Age だけ設定したオブジェクトと、Name だけ設定したオブジェクトが異なる要素として格納される ため、1つのオブジェクトの一部を後から埋めることはできない。
その為new Info{ }
で1つしか指定されていない場合は、指定されなかった値はデフォルトの値である「0」もしくは「空欄」になってしまう。
→別々に代入すると異なるリスト要素になってしまうため、AgeとNameはセットで代入する必要がある。