Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
C# consente di creare un'istanza di un oggetto o di una raccolta ed eseguire assegnazioni di membri in un'unica istruzione.
Inizializzatori di oggetto
Gli inizializzatori di oggetti consentono di assegnare valori a tutti i campi o le proprietà accessibili di un oggetto durante la creazione. Non è necessario richiamare un costruttore e quindi usare le istruzioni di assegnazione. La sintassi dell'inizializzatore di oggetti consente di specificare argomenti per un costruttore o omettere gli argomenti e le parentesi. Per indicazioni sull'uso coerente degli inizializzatori di oggetti, vedere Usare gli inizializzatori di oggetti (regola di stile IDE0017). Nell'esempio seguente viene illustrato come usare un inizializzatore di oggetto con un tipo denominato, Cate come richiamare il costruttore senza parametri. Si noti l'uso di proprietà implementate automaticamente nella Cat classe . Per altre informazioni, vedere Proprietà implementate automaticamente.
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 };
La sintassi dell'inizializzatore di oggetti consente di creare un'istanza e di assegnare l'oggetto appena creato, con le relative proprietà assegnate, alla variabile nell'assegnazione.
A partire dalle proprietà annidate degli oggetti, è possibile usare la sintassi dell'inizializzatore di oggetti senza la new parola chiave . Questa sintassi, Property = { ... }, consente di inizializzare i membri di oggetti annidati esistenti, utili con le proprietà di sola lettura. Per altre informazioni, vedere Inizializzatori di oggetti con proprietà tipizzate dalla classe.
Gli inizializzatori di oggetto possono impostare gli indicizzatori, oltre ad assegnare campi e proprietà. Si consideri questa classe Matrix di base:
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; }
}
}
È possibile inizializzare la matrice di identità usando il codice seguente:
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,
};
Qualsiasi indicizzatore accessibile che contiene un setter accessibile è utilizzabile come espressione in un inizializzatore di oggetto, indipendentemente dal numero o dai tipi degli argomenti. Gli argomenti di indice formano il lato sinistro dell'assegnazione e il valore corrisponde al lato destro dell'espressione. Ad esempio, gli inizializzatori seguenti sono tutti validi se IndexersExample dispone degli indicizzatori appropriati:
var thing = new IndexersExample
{
name = "object one",
[1] = '1',
[2] = '4',
[3] = '9',
Size = Math.PI,
['C',4] = "Middle C"
}
Perché la compilazione del codice precedente riesca, il tipo IndexersExample deve avere i membri seguenti:
public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] { set { ... }; }
Inizializzatori di oggetti con tipi anonimi
Sebbene sia possibile usare gli inizializzatori di oggetto in qualsiasi contesto, sono particolarmente utili nelle espressioni LINQ (Query) di Language-Integrated. Le espressioni di query usano spesso tipi anonimi, che è possibile inizializzare solo usando un inizializzatore di oggetto, come illustrato nella dichiarazione seguente.
var pet = new { Age = 10, Name = "Fluffy" };
Usando tipi anonimi, la select clausola in un'espressione di query LINQ può trasformare gli oggetti della sequenza originale in oggetti il cui valore e la forma differiscono dall'originale. È possibile archiviare solo una parte delle informazioni di ogni oggetto in una sequenza. Nell’esempio seguente, si supponga che un oggetto prodotto (p) contenga molti campi e metodi e che si sia interessati solo alla creazione di una sequenza di oggetti che contengono il nome del prodotto e il prezzo unitario.
var productInfos =
from p in products
select new { p.ProductName, p.UnitPrice };
Quando si esegue questa query, la productInfos variabile contiene una sequenza di oggetti a cui è possibile accedere in un'istruzione foreach , come illustrato in questo esempio:
foreach(var p in productInfos){...}
Ogni oggetto nel nuovo tipo anonimo dispone di due proprietà pubbliche che ricevono gli stessi nomi delle proprietà o dei campi nell'oggetto originale. È anche possibile rinominare un campo quando si crea un tipo anonimo. Nell'esempio seguente il campo viene UnitPrice rinominato in Price.
select new {p.ProductName, Price = p.UnitPrice};
Inizializzatori di oggetti con il required modificatore
Usare la required parola chiave per forzare i chiamanti a impostare il valore di una proprietà o di un campo usando un inizializzatore di oggetto. Non è necessario impostare le proprietà necessarie come parametri del costruttore. Il compilatore garantisce che tutti i chiamanti inizializzino tali valori.
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();
È una procedura tipica per garantire che l'oggetto venga inizializzato correttamente, soprattutto quando si dispone di più campi o proprietà da gestire e non si vuole includerli tutti nel costruttore.
Inizializzatori di oggetti con l'accessor init
Usando una init funzione di accesso, è possibile assicurarsi che l'oggetto non venga modificato dopo l'inizializzazione. Consente di limitare l'impostazione del valore della proprietà.
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";
Le proprietà init-only necessarie supportano strutture non modificabili, consentendo al tempo stesso la sintassi naturale per gli utenti del tipo.
Inizializzatori di oggetti con proprietà tipizzate dalla classe
Quando si inizializzano oggetti con proprietà tipizzate da classi, è possibile usare due sintassi diverse:
-
Inizializzatore di oggetti senza
newparola chiave:Property = { ... } -
Inizializzatore di oggetti con
newparola chiave:Property = new() { ... }
Queste sintassi si comportano in modo diverso. L'esempio seguente illustra entrambi gli approcci:
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
}
Differenze principali
Senza
newparola chiave (ClassB = { BI = 100003 }): questa sintassi modifica l'istanza esistente della proprietà creata dal costruttore dell'oggetto. Chiama inizializzatori di membri sull'oggetto esistente.Con
newparola chiave (ClassB = new() { BI = 100003 }): questa sintassi crea una nuova istanza e la assegna alla proprietà, sostituendo qualsiasi istanza esistente.
L'inizializzatore senza new riutilizza l'istanza corrente. Nell'esempio precedente i valori di ClassB sono: 100003 (nuovo valore assegnato), true (mantenuto dall'inizializzazione di EmbeddedClassTypeA), BBBabc (valore predefinito invariato da EmbeddedClassTypeB).
Inizializzatori di oggetti senza new proprietà di sola lettura
La sintassi senza new è utile con le proprietà di sola lettura. Non è possibile assegnare una nuova istanza, ma è comunque possibile inizializzare i membri dell'istanza esistente:
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}");
}
}
Questo approccio consente di inizializzare oggetti annidati anche quando la proprietà contenitore non ha un setter.
Inizializzatori di raccolta
Gli inizializzatori di raccolta consentono di specificare uno o più inizializzatori di elemento quando si inizializza un tipo di raccolta che implementa IEnumerable e dispone di un Add metodo con la firma appropriata come metodo di istanza o metodo di estensione. Gli inizializzatori di elemento possono essere un valore, un’espressione o un inizializzatore di oggetto. Usando un inizializzatore di raccolta, non è necessario specificare più chiamate; il compilatore aggiunge automaticamente le chiamate. Per indicazioni sull'uso coerente degli inizializzatori di raccolta, vedere Usare gli inizializzatori di raccolta (regola di stile IDE0028). Gli inizializzatori di raccolta sono utili anche nelle query LINQ.
L'esempio seguente illustra due semplici inizializzatori di collezione:
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() };
Nell'inizializzatore di raccolta riportato di seguito vengono utilizzati inizializzatori di oggetto per inizializzare oggetti della classe Cat definiti in un esempio precedente. I singoli inizializzatori di oggetti sono racchiusi tra parentesi graffe e separati da virgole.
List<Cat> cats =
[
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
];
È possibile specificare null come elemento in un inizializzatore di insieme se il metodo Add della raccolta lo consente.
List<Cat?> moreCats = new List<Cat?>
{
new Cat{ Name = "Furrytail", Age=5 },
new Cat{ Name = "Peaches", Age=4 },
null
};
È possibile utilizzare un elemento spread per creare un elenco che copi un altro elenco o altri elenchi.
List<Cat> allCats = [.. cats, .. moreCats];
Includere elementi aggiuntivi oltre all'uso di un elemento spread.
List<Cat> additionalCats = [.. cats, new Cat { Name = "Furrytail", Age = 5 }, .. moreCats];
È possibile specificare elementi indicizzati se la raccolta supporta l'indicizzazione in lettura/scrittura.
var numbers = new Dictionary<int, string>
{
[7] = "seven",
[9] = "nine",
[13] = "thirteen"
};
L'esempio precedente genera codice che chiama Item[TKey] per impostare i valori. È anche possibile inizializzare dizionari e altri contenitori associativi usando la sintassi seguente. Anziché la sintassi dell'indicizzatore, con parentesi e un'assegnazione, usa un oggetto con più valori:
var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
Questo esempio di inizializzatore chiama Add(TKey, TValue) per aggiungere i tre elementi nel dizionario. Questi due modi diversi di inizializzazione delle raccolte associative hanno un comportamento leggermente diverso a causa delle chiamate di metodo generate dal compilatore. Con la classe Dictionary funzionano entrambe le varianti. Altri tipi possono supportare solo uno o l’altro in base all’API pubblica.
Argomenti dell'espressione di raccolta
A partire da C# 15, usare l'elemento with(...) come primo elemento di un'espressione di raccolta per passare argomenti al costruttore della raccolta. Usando questa funzionalità, è possibile specificare capacità, comparer o altri parametri del costruttore direttamente all'interno della sintassi dell'espressione di raccolta:
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");
}
Per altre informazioni sugli argomenti delle espressioni di raccolta, inclusi i tipi e le restrizioni di destinazione supportati, vedere Argomenti dell'espressione di raccolta.
Inizializzatori di oggetti con inizializzazione della proprietà di sola lettura della raccolta
Alcune classi hanno proprietà di raccolta in cui la proprietà è di sola lettura, ad esempio la Cats proprietà di CatOwner nel caso seguente:
public class CatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
Non è possibile usare la sintassi dell'inizializzatore di raccolta descritta in precedenza perché non è possibile assegnare alla proprietà un nuovo elenco:
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 }
}
};
Tuttavia, è possibile aggiungere nuove voci a Cats usando la sintassi di inizializzazione e omettendo la creazione dell'elenco (new List<Cat>), come illustrato nell'esempio seguente:
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
L'insieme di voci da aggiungere viene visualizzato racchiuso tra parentesi graffe. Il codice precedente è identico alla scrittura:
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 });
Esempi
L'esempio seguente unisce i concetti di inizializzatori di oggetto e di insieme.
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.
}
Nell’esempio seguente viene illustrato un oggetto che implementa IEnumerable e contiene un metodo Add con più parametri. Usa un inizializzatore di raccolta con più elementi per elemento nell’elenco che corrispondono alla firma del metodo 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
*/
}
I metodi Add possono usare la parola chiave params per accettare un numero variabile di argomenti, come illustrato nell'esempio seguente. Questo esempio illustra anche l'implementazione personalizzata di un indicizzatore per l'inizializzazione di un insieme tramite indici. A partire da C# 13, il params parametro non è limitato a una matrice. Può essere un tipo o un’interfaccia di raccolta.
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
*/
}