C# を使用すると、オブジェクトまたはコレクションをインスタンス化し、1 つのステートメントでメンバーの割り当てを実行できます。
オブジェクト初期化子
オブジェクト初期化子を使用すると、オブジェクトの作成時に、アクセス可能な任意のフィールドまたはプロパティに値を割り当てることができます。 コンストラクターを呼び出し、代入ステートメントを使用する必要はありません。 オブジェクト初期化子の構文を使用すると、コンストラクターの引数を指定したり、引数とかっこを省略することができます。 オブジェクト初期化子を一貫して使用する方法については、「 オブジェクト初期化子の使用 (スタイル 規則IDE0017)」を参照してください。 次の例は、名前付き型、 Cat、およびパラメーターなしのコンストラクターを呼び出す方法でオブジェクト初期化子を使用する方法を示しています。
Cat クラス内で自動的に実装されたプロパティが使用されています。 詳細については、「自動的に実装されるプロパティ」を参照してください。
public class Cat
{
// Automatically implemented properties.
public int Age { get; set; }
public string? Name { get; set; }
public Cat()
{
}
public Cat(string name)
{
this.Name = name;
}
}
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };
オブジェクト初期化子の構文を使用すると、インスタンスを作成し、割り当てられたプロパティを持つ新しく作成されたオブジェクトを代入の変数に割り当てることができます。
入れ子になったオブジェクト プロパティ以降では、 new キーワードなしでオブジェクト初期化子構文を使用できます。 この構文 Property = { ... }、既存の入れ子になったオブジェクトのメンバーを初期化できます。これは、読み取り専用プロパティで役立ちます。 詳細については、「 クラス型のプロパティを持つオブジェクト初期化子」を参照してください。
オブジェクト初期化子では、フィールドとプロパティを割り当てることに加え、インデクサーを設定できます。 次の基底 Matrix クラスをご覧ください。
public class Matrix
{
private double[,] storage = new double[3, 3];
public double this[int row, int column]
{
// The embedded array will throw out of range exceptions as appropriate.
get { return storage[row, column]; }
set { storage[row, column] = value; }
}
}
次のコードを使用して、ID マトリックスを初期化できます。
var identity = new Matrix
{
[0, 0] = 1.0,
[0, 1] = 0.0,
[0, 2] = 0.0,
[1, 0] = 0.0,
[1, 1] = 1.0,
[1, 2] = 0.0,
[2, 0] = 0.0,
[2, 1] = 0.0,
[2, 2] = 1.0,
};
アクセス可能なセッターが含まれるアクセス可能なインデクサーを、引数の数や種類と関係なく、オブジェクト初期化子で式の 1 つとして使用できます。 インデックス引数は代入の左側となり、値は式の右側となります。 たとえば、IndexersExample に適切なインデクサーがある場合、次の初期化子はすべて有効です。
var thing = new IndexersExample
{
name = "object one",
[1] = '1',
[2] = '4',
[3] = '9',
Size = Math.PI,
['C',4] = "Middle C"
}
上記のコードをコンパイルするには、IndexersExample 型に次のメンバーを指定する必要があります。
public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] { set { ... }; }
匿名型のオブジェクト初期化子
オブジェクト初期化子は任意のコンテキストで使用できますが、Language-Integrated クエリ (LINQ) 式では特に便利です。 クエリ式では、次の宣言に示すように、オブジェクト初期化子を使用してのみ初期化できる 匿名型が頻繁に使用されます。
var pet = new { Age = 10, Name = "Fluffy" };
LINQ クエリ式の select 句は、匿名型を使用して、元のシーケンスのオブジェクトを、元の値と形状が異なるオブジェクトに変換できます。 シーケンス内の各オブジェクトの情報の一部のみ保存することを、お勧めします。 次の例は、製品オブジェクト (p) に多くのフィールドおよびメソッドが含まれており、製品名および単価を含むオブジェクトのシーケンスを作成することにのみ関心があることを想定しています。
var productInfos =
from p in products
select new { p.ProductName, p.UnitPrice };
このクエリを実行すると、 productInfos 変数には、次の例に示すように、 foreach ステートメントでアクセスできるオブジェクトのシーケンスが含まれます。
foreach(var p in productInfos){...}
作成される匿名型内の各オブジェクトには、2 つのパブリック プロパティがあります。これらのプロパティには、元のオブジェクトのプロパティまたはフィールドと同じ名前が付けられます。 匿名型を作成するときに、フィールドの名前を変更することもできます。 次の例では、 UnitPrice フィールドの名前を Price に変更します。
select new {p.ProductName, Price = p.UnitPrice};
required修飾子を持つオブジェクト初期化子
オブジェクト初期化子を使用してプロパティまたはフィールドの値を設定するように呼び出し元に強制するには、 required キーワードを使用します。 必要なプロパティをコンストラクター パラメーターとして設定する必要はありません。 コンパイラは、すべての呼び出し元によってこれらの値が確実に初期化されるようにします。
public class Pet
{
public required int Age;
public string Name;
}
// `Age` field is necessary to be initialized.
// You don't need to initialize `Name` property
var pet = new Pet() { Age = 10};
// Compiler error:
// Error CS9035 Required member 'Pet.Age' must be set in the object initializer or attribute constructor.
// var pet = new Pet();
特に、管理するフィールドまたはプロパティが複数あり、それらをすべてコンストラクターに含めたくない場合は、オブジェクトが適切に初期化されていることを保証するのが一般的な方法です。
init アクセサーを使用したオブジェクト初期化子
init アクセサーを使用すると、初期化後にオブジェクトが変更されないようにすることができます。 これにより、プロパティ値の設定を制限できます。
public class Person
{
public string FirstName { get; set; }
public string LastName { get; init; }
}
// The `LastName` property can be set only during initialization. It CAN'T be modified afterwards.
// The `FirstName` property can be modified after initialization.
var pet = new Person() { FirstName = "Joe", LastName = "Doe"};
// You can assign the FirstName property to a different value.
pet.FirstName = "Jane";
// Compiler error:
// Error CS8852 Init - only property or indexer 'Person.LastName' can only be assigned in an object initializer,
// or on 'this' or 'base' in an instance constructor or an 'init' accessor.
// pet.LastName = "Kowalski";
必須の init-only プロパティは、型のユーザーに対して自然な構文を許可しながら、不変構造体をサポートします。
クラス型のプロパティを持つオブジェクト初期化子
クラス型のプロパティを使用してオブジェクトを初期化する場合は、次の 2 つの異なる構文を使用できます。
-
newキーワードのないオブジェクト初期化子:Property = { ... } -
newキーワードを持つオブジェクト初期化子:Property = new() { ... }
これらの構文の動作は異なります。 次の例は、両方の方法を示しています。
public class HowToClassTypedInitializer
{
public class EmbeddedClassTypeA
{
public int I { get; set; }
public bool B { get; set; }
public string S { get; set; }
public EmbeddedClassTypeB ClassB { get; set; }
public override string ToString() => $"{I}|{B}|{S}|||{ClassB}";
public EmbeddedClassTypeA()
{
Console.WriteLine($"Entering EmbeddedClassTypeA constructor. Values are: {this}");
I = 3;
B = true;
S = "abc";
ClassB = new() { BB = true, BI = 43 };
Console.WriteLine($"Exiting EmbeddedClassTypeA constructor. Values are: {this})");
}
}
public class EmbeddedClassTypeB
{
public int BI { get; set; }
public bool BB { get; set; }
public string BS { get; set; }
public override string ToString() => $"{BI}|{BB}|{BS}";
public EmbeddedClassTypeB()
{
Console.WriteLine($"Entering EmbeddedClassTypeB constructor. Values are: {this}");
BI = 23;
BB = false;
BS = "BBBabc";
Console.WriteLine($"Exiting EmbeddedClassTypeB constructor. Values are: {this})");
}
}
public static void Main()
{
var a = new EmbeddedClassTypeA
{
I = 103,
B = false,
ClassB = { BI = 100003 }
};
Console.WriteLine($"After initializing EmbeddedClassTypeA: {a}");
var a2 = new EmbeddedClassTypeA
{
I = 103,
B = false,
ClassB = new() { BI = 100003 } //New instance
};
Console.WriteLine($"After initializing EmbeddedClassTypeA a2: {a2}");
}
// Output:
//Entering EmbeddedClassTypeA constructor Values are: 0|False||||
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
//After initializing EmbeddedClassTypeA: 103|False|abc|||100003|True|BBBabc
//Entering EmbeddedClassTypeA constructor Values are: 0|False||||
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//After initializing EmbeddedClassTypeA a2: 103|False|abc|||100003|False|BBBabc
}
主な違い
newキーワード (ClassB = { BI = 100003 })がない場合: この構文は、オブジェクトコンストラクターが作成したプロパティの既存のインスタンスを変更します。 既存のオブジェクトに対してメンバー初期化子を呼び出します。newキーワード (ClassB = new() { BI = 100003 }): この構文は、新しいインスタンスを作成し、既存のインスタンスを置き換えてプロパティに割り当てます。
newを使用しない初期化子は、現在のインスタンスを再利用します。 前の例では、ClassB の値は、 100003 (新しい値の割り当て)、 true (EmbeddedClassTypeA の初期化から保持)、 BBBabc (EmbeddedClassTypeB からの既定値は変更されません) です。
読み取り専用プロパティの new のないオブジェクト初期化子
newを使用しない構文は、読み取り専用プロパティで役立ちます。 新しいインスタンスを割り当てることはできませんが、既存のインスタンスのメンバーを初期化することはできます。
public class ReadOnlyPropertyExample
{
public class Settings
{
public string Theme { get; set; } = "Light";
public int FontSize { get; set; } = 12;
}
public class Application
{
public string Name { get; set; } = "";
// This property is read-only - it can only be set during construction
public Settings AppSettings { get; } = new();
}
public static void Example()
{
// You can still initialize the nested object's properties
// even though AppSettings property has no setter
var app = new Application
{
Name = "MyApp",
AppSettings = { Theme = "Dark", FontSize = 14 }
};
// This would cause a compile error because AppSettings has no setter:
// app.AppSettings = new Settings { Theme = "Dark", FontSize = 14 };
Console.WriteLine($"App: {app.Name}, Theme: {app.AppSettings.Theme}, Font Size: {app.AppSettings.FontSize}");
}
}
この方法では、格納プロパティにセッターがない場合でも、入れ子になったオブジェクトを初期化できます。
コレクション初期化子
コレクション初期化子を使用すると、 IEnumerable を実装し、インスタンス メソッドまたは拡張メソッドとして適切なシグネチャを持つ Add メソッドを持つコレクション型を初期化するときに、1 つ以上の要素初期化子を指定できます。 要素の初期化子は、値、式またはオブジェクト初期化子です。 コレクション初期化子を使用すると、コンパイラによって呼び出しが自動的に追加されるため、呼び出しを複数回指定する必要がなくなります。 コレクション初期化子を一貫して使用する方法については、「 コレクション初期化子の使用 (スタイル 規則IDE0028)」を参照してください。 コレクション初期化子は、 LINQ クエリでも役立ちます。
2 つの単純なコレクション初期化子を次の例に示します。
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> digits2 = new List<int> { 0 + 1, 12 % 3, MakeInt() };
次のコレクション初期化子は、前の例で定義されている Cat クラスのオブジェクトをオブジェクト初期化子を使用して初期化します。 個々のオブジェクト初期化子は、かっこで囲まれ、コンマで区切られています。
List<Cat> cats =
[
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
];
コレクションの メソッドで許容されている場合、コレクション初期化子の要素として Add を指定できます。
List<Cat?> moreCats = new List<Cat?>
{
new Cat{ Name = "Furrytail", Age=5 },
new Cat{ Name = "Peaches", Age=4 },
null
};
スプレッド要素を使用して、他のリストまたはリストをコピーする 1 つのリストを作成できます。
List<Cat> allCats = [.. cats, .. moreCats];
また、拡散要素の使用と共に追加の要素を含めます。
List<Cat> additionalCats = [.. cats, new Cat { Name = "Furrytail", Age = 5 }, .. moreCats];
コレクションがインデックスを読み取り/書き込みできる場合は、インデックス付きの要素を指定できます。
var numbers = new Dictionary<int, string>
{
[7] = "seven",
[9] = "nine",
[13] = "thirteen"
};
上記のサンプルの場合、Item[TKey] を呼び出して値を設定するコードが生成されます。 次の構文を使用して、ディクショナリやその他の連想コンテナーを初期化することもできます。 かっこと代入を使用するインデクサー構文の代わりに、複数の値を持つオブジェクトを使用します。
var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
この初期化子の例では、Add(TKey, TValue) を呼び出し、3 つの項目をディクショナリに追加しています。 連想コレクションを初期化するこれら 2 つの異なる方法には、コンパイラによって生成されるメソッド呼び出しにより、動作にわずかな違いがあります。 いずれの場合も Dictionary クラスが使用されます。 他の型の場合、パブリック API に基づいて、いずれかのみサポートされることがあります。
コレクション式の引数
C# 15 以降では、コレクション式の最初の要素として with(...) 要素を使用して 、コレクション のコンストラクターに引数を渡します。 この機能を使用すると、容量、比較子、またはその他のコンストラクター パラメーターをコレクション式の構文内で直接指定できます。
public static void CollectionExpressionWithArgumentsExample()
{
string[] values = ["one", "two", "three"];
// Use with() to pass capacity to the List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];
Console.WriteLine($"Created List<string> with capacity: {names.Capacity}");
Console.WriteLine($"List contains {names.Count} elements: [{string.Join(", ", names)}]");
// Use with() to pass a comparer to the HashSet<T> constructor
HashSet<string> caseInsensitiveSet = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO"];
// caseInsensitiveSet contains only one element because "Hello" and "HELLO" are equal
Console.WriteLine($"HashSet with case-insensitive comparer contains {caseInsensitiveSet.Count} elements: [{string.Join(", ", caseInsensitiveSet)}]");
Console.WriteLine("Note: 'Hello' and 'HELLO' are treated as the same element due to OrdinalIgnoreCase comparer");
}
サポートされているターゲットの型や制限など、コレクション式の引数の詳細については、「 コレクション式の引数」を参照してください。
コレクションの読み取り専用プロパティ初期化を使用するオブジェクト初期化子
一部のクラスには、次の場合のCatsのCatOwner プロパティのように、プロパティが読み取り専用のコレクション プロパティがあります。
public class CatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
プロパティに新しいリストを割り当てることができないため、前に説明したコレクション初期化子構文を使用することはできません。
CatOwner owner = new CatOwner
{
Cats = new List<Cat>
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
ただし、次の例に示すように、初期化構文を使用し、リストの作成 (Cats) を省略することで、新しいエントリをnew List<Cat>に追加できます。
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
追加するエントリのグループは、中かっこで囲まれて表示されます。 上のコードは次のように書くのと同じです。
CatOwner owner = new ();
owner.Cats.Add(new Cat{ Name = "Sylvester", Age=8 });
owner.Cats.Add(new Cat{ Name = "Whiskers", Age=2 });
owner.Cats.Add(new Cat{ Name = "Sasha", Age=14 });
例
次の例では、オブジェクトの概念とコレクション初期化子の概念が組み合わさっています。
public class InitializationSample
{
public class Cat
{
// Automatically implemented properties.
public int Age { get; set; }
public string? Name { get; set; }
public Cat() { }
public Cat(string name)
{
Name = name;
}
}
public static void Main()
{
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };
List<Cat> cats =
[
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
];
List<Cat?> moreCats = new List<Cat?>
{
new Cat { Name = "Furrytail", Age = 5 },
new Cat { Name = "Peaches", Age = 4 },
null
};
List<Cat> allCats = [.. cats, new Cat { Name = "Łapka", Age = 5 }, cat, .. moreCats];
// Display results.
foreach (Cat? c in allCats)
{
if (c != null)
{
System.Console.WriteLine(c.Name);
}
else
{
System.Console.WriteLine("List element has null value.");
}
}
}
// Output:
// Sylvester
// Whiskers
// Sasha
// Łapka
// Fluffy
// Furrytail
// Peaches
// List element has null value.
}
次に示すのは、IEnumerable を実装し、複数のパラメーターを持つ Add メソッドを含むオブジェクトの例です。
Add メソッドのシグネチャに対応するリスト内の項目ごとに複数の要素を持つコレクション初期化子を使っています。
public class FullExample
{
class FormattedAddresses : IEnumerable<string>
{
private List<string> internalList = new();
public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();
public void Add(string firstname, string lastname,
string street, string city,
string state, string zipcode) => internalList.Add($"""
{firstname} {lastname}
{street}
{city}, {state} {zipcode}
"""
);
}
public static void Main()
{
FormattedAddresses addresses = new FormattedAddresses()
{
{"John", "Doe", "123 Street", "Topeka", "KS", "00000" },
{"Jane", "Smith", "456 Street", "Topeka", "KS", "00000" }
};
Console.WriteLine("Address Entries:");
foreach (string addressEntry in addresses)
{
Console.WriteLine("\r\n" + addressEntry);
}
}
/*
* Prints:
Address Entries:
John Doe
123 Street
Topeka, KS 00000
Jane Smith
456 Street
Topeka, KS 00000
*/
}
Add メソッドでは、次の例で示すように、params キーワードを使用して可変数個の引数を受け取ることができます。 この例では、インデクサーのカスタム実装と、インデクサーを使用したコレクションの初期化を示しています。 C# 13 以降では、 params パラメーターは配列に制限されません。 コレクション型やインターフェイスにも対応します。
public class DictionaryExample
{
class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>> where TKey : notnull
{
private Dictionary<TKey, List<TValue>> internalDictionary = new Dictionary<TKey, List<TValue>>();
public IEnumerator<KeyValuePair<TKey, List<TValue>>> GetEnumerator() => internalDictionary.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalDictionary.GetEnumerator();
public List<TValue> this[TKey key]
{
get => internalDictionary[key];
set => Add(key, value);
}
public void Add(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);
public void Add(TKey key, IEnumerable<TValue> values)
{
if (!internalDictionary.TryGetValue(key, out List<TValue>? storedValues))
{
internalDictionary.Add(key, storedValues = new());
}
storedValues.AddRange(values);
}
}
public static void Main()
{
RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary1
= new RudimentaryMultiValuedDictionary<string, string>()
{
{"Group1", "Bob", "John", "Mary" },
{"Group2", "Eric", "Emily", "Debbie", "Jesse" }
};
RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary2
= new RudimentaryMultiValuedDictionary<string, string>()
{
["Group1"] = new List<string>() { "Bob", "John", "Mary" },
["Group2"] = new List<string>() { "Eric", "Emily", "Debbie", "Jesse" }
};
RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary3
= new RudimentaryMultiValuedDictionary<string, string>()
{
{"Group1", new string []{ "Bob", "John", "Mary" } },
{ "Group2", new string[]{ "Eric", "Emily", "Debbie", "Jesse" } }
};
Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");
foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary1)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member in group.Value)
{
Console.WriteLine(member);
}
}
Console.WriteLine("\r\nUsing second multi-valued dictionary created with a collection initializer using indexing:");
foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary2)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member in group.Value)
{
Console.WriteLine(member);
}
}
Console.WriteLine("\r\nUsing third multi-valued dictionary created with a collection initializer using indexing:");
foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary3)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member in group.Value)
{
Console.WriteLine(member);
}
}
}
/*
* Prints:
Using first multi-valued dictionary created with a collection initializer:
Members of group Group1:
Bob
John
Mary
Members of group Group2:
Eric
Emily
Debbie
Jesse
Using second multi-valued dictionary created with a collection initializer using indexing:
Members of group Group1:
Bob
John
Mary
Members of group Group2:
Eric
Emily
Debbie
Jesse
Using third multi-valued dictionary created with a collection initializer using indexing:
Members of group Group1:
Bob
John
Mary
Members of group Group2:
Eric
Emily
Debbie
Jesse
*/
}
.NET