Object and Collection Initializers (C# Programming Guide)
Άρθρο
C# lets you instantiate an object or collection and perform member assignments in a single statement.
Object initializers
Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements. The object initializer syntax enables you to specify arguments for a constructor or omit the arguments (and parentheses syntax). The following example shows how to use an object initializer with a named type, Cat and how to invoke the parameterless constructor. Note the use of automatically implemented properties in the Cat class. For more information, see Automatically implemented properties.
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };
The object initializers syntax allows you to create an instance, and after that it assigns the newly created object, with its assigned properties, to the variable in the assignment.
Object initializers can set indexers, in addition to assigning fields and properties. Consider this basic Matrix class:
C#
publicclassMatrix
{
privatedouble[,] storage = newdouble[3, 3];
publicdoublethis[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; }
}
}
You could initialize the identity matrix with the following code:
Any accessible indexer that contains an accessible setter can be used as one of the expressions in an object initializer, regardless of the number or types of arguments. The index arguments form the left side of the assignment, and the value is the right side of the expression. For example, the following initializers are all valid if IndexersExample has the appropriate indexers:
C#
var thing = new IndexersExample
{
name = "object one",
[1] = '1',
[2] = '4',
[3] = '9',
Size = Math.PI,
['C',4] = "Middle C"
}
For the preceding code to compile, the IndexersExample type must have the following members:
C#
publicstring name;
publicdouble Size { set { ... }; }
publiccharthis[int i] { set { ... }; }
publicstringthis[char c, int i] { set { ... }; }
Object Initializers with anonymous types
Although object initializers can be used in any context, they're especially useful in LINQ query expressions. Query expressions make frequent use of anonymous types, which can only be initialized by using an object initializer, as shown in the following declaration.
C#
var pet = new { Age = 10, Name = "Fluffy" };
Anonymous types enable the select clause in a LINQ query expression to transform objects of the original sequence into objects whose value and shape can differ from the original. You might want to store only a part of the information from each object in a sequence. In the following example, assume that a product object (p) contains many fields and methods, and that you're only interested in creating a sequence of objects that contain the product name and the unit price.
C#
var productInfos =
from p in products
selectnew { p.ProductName, p.UnitPrice };
When this query is executed, the productInfos variable contains a sequence of objects that can be accessed in a foreach statement as shown in this example:
C#
foreach(var p in productInfos){...}
Each object in the new anonymous type has two public properties that receive the same names as the properties or fields in the original object. You can also rename a field when you're creating an anonymous type; the following example renames the UnitPrice field to Price.
C#
selectnew {p.ProductName, Price = p.UnitPrice};
Object Initializers with the required modifier
You use the required keyword to force callers to set the value of a property or field using an object initializer. Required properties don't need to be set as constructor parameters. The compiler ensures all callers initialize those values.
C#
publicclassPet
{
public required int Age;
publicstring Name;
}
// `Age` field is necessary to be initialized.// You don't need to initialize `Name` propertyvar 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();
It's a typical practice to guarantee that your object is properly initialized, especially when you have multiple fields or properties to manage and don't want to include them all in the constructor.
Object Initializers with the init accessor
Making sure no one changes the designed object could be limited by using an init accessor. It helps to restrict the setting of the property value.
C#
publicclassPerson
{
publicstring FirstName { get; set; }
publicstring 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";
Required init-only properties support immutable structures while allowing natural syntax for users of the type.
Object Initializers with class-typed properties
It's crucial to consider the implications for class-typed properties when initializing an object:
The following example shows how, for ClassB, the initialization process involves updating specific values while retaining others from the original instance. The Initializer reuses current instance: ClassB's values are: 100003 (new value we assign here), true (kept from EmbeddedClassTypeA's initialization), BBBabc (unchanged default from EmbeddedClassTypeB).
Collection initializers
Collection initializers let you specify one or more element initializers when you initialize a collection type that implements IEnumerable and has Add with the appropriate signature as an instance method or an extension method. The element initializers can be a value, an expression, or an object initializer. By using a collection initializer, you don't have to specify multiple calls; the compiler adds the calls automatically.
The following example shows two simple collection initializers:
The following collection initializer uses object initializers to initialize objects of the Cat class defined in a previous example. The individual object initializers are enclosed in braces and separated by commas.
C#
List<Cat> cats = new List<Cat>
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
};
You can specify null as an element in a collection initializer if the collection's Add method allows it.
C#
List<Cat?> moreCats = new List<Cat?>
{
new Cat{ Name = "Furrytail", Age=5 },
new Cat{ Name = "Peaches", Age=4 },
null
};
You can use a spread element to create one list that copies other list or lists.
C#
List<Cat> allCats = [.. cats, .. moreCats];
And include additonal elements along with using a spread element.
C#
List<Cat> additionalCats = [.. cats, new Cat { Name = "Furrytail", Age = 5 }, .. moreCats];
You can specify indexed elements if the collection supports read / write indexing.
C#
var numbers = new Dictionary<int, string>
{
[7] = "seven",
[9] = "nine",
[13] = "thirteen"
};
The preceding sample generates code that calls the Item[TKey] to set the values. You could also initialize dictionaries and other associative containers using the following syntax. Notice that instead of indexer syntax, with parentheses and an assignment, it uses an object with multiple values:
C#
var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
This initializer example calls Add(TKey, TValue) to add the three items into the dictionary. These two different ways to initialize associative collections have slightly different behavior because of the method calls the compiler generates. Both variants work with the Dictionary class. Other types might only support one or the other based on their public API.
Object Initializers with collection read-only property initialization
Some classes might have collection properties where the property is read-only, like the Cats property of CatOwner in the following case:
C#
publicclassCatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
You can't use collection initializer syntax discussed so far since the property can't be assigned a new list:
C#
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 }
}
};
However, new entries can be added to Cats nonetheless using the initialization syntax by omitting the list creation (new List<Cat>), as shown next:
C#
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
The set of entries to be added appear surrounded by braces. The preceding code is identical to writing:
C#
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 });
Examples
The following example combines the concepts of object and collection initializers.
C#
publicclassInitializationSample
{
publicclassCat
{
// Automatically implemented properties.publicint Age { get; set; }
publicstring? Name { get; set; }
publicCat() { }
publicCat(string name)
{
Name = name;
}
}
publicstaticvoidMain()
{
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };
List<Cat> cats = new List<Cat>
{
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.
}
The following example shows an object that implements IEnumerable and contains an Add method with multiple parameters. It uses a collection initializer with multiple elements per item in the list that correspond to the signature of the Add method.
C#
publicclassFullExample
{
classFormattedAddresses : IEnumerable<string>
{
private List<string> internalList = new List<string>();
public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();
publicvoidAdd(string firstname, string lastname,
string street, string city,
string state, string zipcode) => internalList.Add($"""
{firstname} {lastname}
{street}
{city}, {state} {zipcode}
"""
);
}
publicstaticvoidMain()
{
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 methods can use the params keyword to take a variable number of arguments, as shown in the following example. This example also demonstrates the custom implementation of an indexer to initialize a collection using indexes. Beginning with C# 13, the params parameter isn't restricted to an array. It can be a collection type or interface.
C#
publicclassDictionaryExample
{
classRudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>> whereTKey : 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);
}
publicvoidAdd(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);
publicvoidAdd(TKey key, IEnumerable<TValue> values)
{
if (!internalDictionary.TryGetValue(key, out List<TValue>? storedValues))
{
internalDictionary.Add(key, storedValues = new List<TValue>());
}
storedValues.AddRange(values);
}
}
publicstaticvoidMain()
{
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", newstring []{ "Bob", "John", "Mary" } },
{ "Group2", newstring[]{ "Eric", "Emily", "Debbie", "Jesse" } }
};
Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");
foreach (KeyValuePair<string, List<string>> groupin rudimentaryMultiValuedDictionary1)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member ingroup.Value)
{
Console.WriteLine(member);
}
}
Console.WriteLine("\r\nUsing second multi-valued dictionary created with a collection initializer using indexing:");
foreach (KeyValuePair<string, List<string>> groupin rudimentaryMultiValuedDictionary2)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member ingroup.Value)
{
Console.WriteLine(member);
}
}
Console.WriteLine("\r\nUsing third multi-valued dictionary created with a collection initializer using indexing:");
foreach (KeyValuePair<string, List<string>> groupin rudimentaryMultiValuedDictionary3)
{
Console.WriteLine($"\r\nMembers of group {group.Key}: ");
foreach (string member ingroup.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
*/
}
Μπορείτε να βρείτε την πηγή για αυτό το περιεχόμενο στο GitHub, όπου μπορείτε επίσης να δημιουργήσετε και να εξετάσετε ζητήματα και αιτήματα έλξης. Για περισσότερες πληροφορίες, ανατρέξτε στον οδηγό συνεργατών.
.NET σχόλια
.NET είναι ένα έργο ανοιχτού κώδικα. Επιλέξτε μια σύνδεση για να παρέχετε σχόλια:
Συμμετάσχετε στη σειρά meetup για να δημιουργήσετε κλιμακούμενες λύσεις AI που βασίζονται σε πραγματικές περιπτώσεις χρήσης με συναδέλφους προγραμματιστές και ειδικούς.
Learn how to use object initializers to initialize type objects in C# without invoking a constructor. Use an object initializer to define an anonymous type.
These examples illustrate using properties in C#. See how the get and set accessors implement read and write access and find out about uses for properties.
For an automatically implemented property in C#, the compiler creates a private, anonymous backing field accessed only through get and set accessors of the property.