Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O C# permite-lhe instanciar um objeto ou coleção e realizar atribuições de membros numa única instrução.
Inicializadores de objeto
Os inicializadores de objetos permitem-te atribuir valores a quaisquer campos ou propriedades acessíveis de um objeto quando o crias. Não precisas de invocar um construtor e depois usar instruções de atribuição. A sintaxe do inicializador de objetos permite-lhe especificar argumentos para um construtor ou omitir os argumentos e parênteses. Para orientações sobre como usar inicializadores de objetos de forma consistente, veja Usar inicializadores de objetos (regra de estilo IDE0017). O exemplo seguinte mostra como usar um inicializador de objetos com um tipo nomeado, Cat, e como invocar o construtor sem parâmetros. Observe o uso de propriedades implementadas automaticamente na Cat classe. Para obter mais informações, consulte Propriedades implementadas 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 };
A sintaxe do inicializador de objetos permite-lhe criar uma instância e atribuir o objeto recém-criado, com as propriedades atribuídas, à variável na atribuição.
Começando com propriedades de objetos aninhados, pode-se usar a sintaxe de inicialização de objeto sem a new keyword. Esta sintaxe, Property = { ... }, permite-lhe inicializar membros de objetos aninhados existentes, o que é útil com propriedades de apenas leitura. Para obter mais informações, consulte Inicializadores de objeto com propriedades tipadas por classe.
Os inicializadores de objetos podem definir indexadores, além de atribuir campos e propriedades. Considere esta classe básica 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; }
}
}
Pode inicializar a matriz identidade usando o seguinte código:
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,
};
Qualquer indexador acessível que contenha um setter acessível pode ser usado como uma das expressões em um inicializador de objeto, independentemente do número ou tipos de argumentos. Os argumentos de índice formam o lado esquerdo da atribuição e o valor é o lado direito da expressão. Por exemplo, os seguintes inicializadores são todos válidos se IndexersExample tiver os indexadores apropriados:
var thing = new IndexersExample
{
name = "object one",
[1] = '1',
[2] = '4',
[3] = '9',
Size = Math.PI,
['C',4] = "Middle C"
}
Para que o código anterior seja compilado, o IndexersExample tipo deve ter os seguintes membros:
public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] { set { ... }; }
Inicializadores de objetos com tipos anónimos
Embora possas usar inicializadores de objetos em qualquer contexto, são especialmente úteis em expressões Language-Integrated Query (LINQ). Expressões de consulta usam frequentemente tipos anónimos, que só pode inicializar usando um inicializador de objetos, como mostrado na declaração seguinte.
var pet = new { Age = 10, Name = "Fluffy" };
Ao usar tipos anónimos, a select cláusula numa expressão de consulta LINQ pode transformar objetos da sequência original em objetos cujo valor e forma diferem do original. Talvez você queira armazenar apenas uma parte das informações de cada objeto em uma sequência. No exemplo a seguir, suponha que um objeto de produto (p) contém muitos campos e métodos e que você só está interessado em criar uma sequência de objetos que contêm o nome do produto e o preço unitário.
var productInfos =
from p in products
select new { p.ProductName, p.UnitPrice };
Quando executa esta consulta, a productInfos variável contém uma sequência de objetos à qual pode aceder numa foreach instrução, como mostrado neste exemplo:
foreach(var p in productInfos){...}
Cada objeto no novo tipo anônimo tem duas propriedades públicas que recebem os mesmos nomes que as propriedades ou campos no objeto original. Também pode renomear um campo quando cria um tipo anónimo. O exemplo seguinte renomeia o UnitPrice corpo para Price.
select new {p.ProductName, Price = p.UnitPrice};
Inicializadores de objetos com o modificador required
Use a required palavra-chave para forçar os chamadores a definir o valor de uma propriedade ou campo usando um inicializador de objetos. Não precisas de definir as propriedades necessárias como parâmetros do construtor. O compilador garante que todos os chamadores inicializem esses valores.
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();
É uma prática típica garantir que seu objeto seja inicializado corretamente, especialmente quando você tem vários campos ou propriedades para gerenciar e não deseja incluí-los todos no construtor.
Inicializadores de objetos com o init acessório
Ao usar um init acessório, podes garantir que o objeto não muda após a inicialização. Isto ajuda a limitar o valor da propriedade.
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";
As propriedades somente de inicialização necessárias suportam estruturas imutáveis, permitindo sintaxe natural para usuários do tipo.
Inicializadores de objetos com propriedades do tipo classe
Quando inicializas objetos com propriedades de tipo de classe, podes usar duas sintaxes diferentes:
-
Inicializador de objeto sem
newpalavra-chave:Property = { ... } -
Inicializador de objeto com
newpalavra-chave:Property = new() { ... }
Estas sintaxes comportam-se de forma diferente. O exemplo a seguir demonstra ambas as abordagens:
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
}
Diferenças principais
Sem
newa palavra-chave (ClassB = { BI = 100003 }): Esta sintaxe modifica a instância existente da propriedade que o construtor do objeto criou. Invoca inicializadores de membro no objeto existente.Com
newpalavra-chave (ClassB = new() { BI = 100003 }): Esta sintaxe cria uma nova instância e atribui-a à propriedade, substituindo qualquer instância existente.
O inicializador sem new reutiliza a instância atual. No exemplo anterior, os valores de ClassB são: 100003 (novo valor atribuído), true (mantido da inicialização de EmbeddedClassTypeA), BBBabc (padrão inalterado de EmbeddedClassTypeB).
Inicializadores de objeto sem new para propriedades somente leitura
A sintaxe sem new é útil com propriedades somente de leitura. Não podes atribuir uma nova instância, mas ainda podes inicializar os membros da instância existente:
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}");
}
}
Essa abordagem permite inicializar objetos aninhados mesmo quando a propriedade que contém não tem um setter.
Inicializadores de coleção
Os inicializadores de coleções permitem-lhe especificar um ou mais inicializadores de elementos ao inicializar um tipo de coleção que implementa IEnumerable e tem um Add método com a assinatura apropriada como método de instância ou método de extensão. Os inicializadores de elemento podem ser um valor, uma expressão ou um inicializador de objeto. Usando um inicializador de coleção, você não precisa especificar várias chamadas; O compilador adiciona as chamadas automaticamente. Para orientações sobre como usar inicializadores de coleção de forma consistente, veja Usar inicializadores de coleção (regra de estilo IDE0028). Os inicializadores de coleção também são úteis em consultas LINQ.
O exemplo a seguir mostra dois inicializadores de coleção simples:
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() };
O inicializador de coleção a seguir usa inicializadores de objeto para inicializar objetos da Cat classe definida em um exemplo anterior. Os inicializadores de objetos individuais são colocados entre chaves e separados por vírgulas.
List<Cat> cats =
[
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
];
Você pode especificar null como um elemento em um inicializador de coleção se o método da Add coleção permitir.
List<Cat?> moreCats = new List<Cat?>
{
new Cat{ Name = "Furrytail", Age=5 },
new Cat{ Name = "Peaches", Age=4 },
null
};
Você pode usar um elemento spread para criar uma lista que copie outra lista ou listas.
List<Cat> allCats = [.. cats, .. moreCats];
Incluir elementos adicionais juntamente com o uso de um elemento de propagação.
List<Cat> additionalCats = [.. cats, new Cat { Name = "Furrytail", Age = 5 }, .. moreCats];
Você pode especificar elementos indexados se a coleção oferecer suporte à indexação de leitura/gravação.
var numbers = new Dictionary<int, string>
{
[7] = "seven",
[9] = "nine",
[13] = "thirteen"
};
O exemplo anterior gera código que chama o Item[TKey] para definir os valores. Também pode inicializar dicionários e outros recipientes associativos usando a seguinte sintaxe. Em vez da sintaxe do indexador, com parênteses e uma atribuição, utiliza um objeto com múltiplos valores:
var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
Este exemplo de inicializador chama Add(TKey, TValue) para adicionar os três itens ao dicionário. Essas duas maneiras diferentes de inicializar coleções associativas têm um comportamento ligeiramente diferente por causa das chamadas de método que o compilador gera. Ambas as variantes funcionam com a Dictionary classe. Outros tipos podem oferecer suporte apenas a um ou outro com base em sua API pública.
Argumentos de expressão de coleções
A partir de C# 15, use o with(...) elemento como primeiro elemento numa expressão de coleção para passar argumentos ao construtor da coleção. Ao usar esta funcionalidade, pode especificar capacidade, comparadores ou outros parâmetros do construtor diretamente dentro da sintaxe da expressão de coleção:
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");
}
Para mais informações sobre argumentos de expressão de coleção, incluindo tipos de alvos suportados e restrições, veja Argumentos de expressão de coleção.
Inicializadores de objetos com inicialização de propriedades de coleção somente leitura
Algumas classes têm propriedades de coleção onde a propriedade é apenas de leitura, como a Cats propriedade de CatOwner no seguinte caso:
public class CatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
Não pode usar a sintaxe do inicializador de coleção discutida anteriormente, pois a propriedade não pode ser atribuída a uma nova lista:
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 }
}
};
No entanto, pode adicionar novas entradas a Cats usando a sintaxe de inicialização e omitindo a criação da lista (new List<Cat>), como mostrado no exemplo seguinte:
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
O conjunto de entradas a adicionar aparece rodeado por órteses. O código anterior é idêntico ao seguinte exemplo:
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 });
Exemplos
O exemplo a seguir combina os conceitos de inicializadores de objeto e coleção.
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.
}
O exemplo a seguir mostra um objeto que implementa IEnumerable e contém um Add método com vários parâmetros. Ele usa um inicializador de coleção com vários elementos por item na lista que correspondem à assinatura do método 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 métodos podem usar a params palavra-chave para obter um número variável de argumentos, como mostrado no exemplo a seguir. Este exemplo também demonstra a implementação personalizada de um indexador para inicializar uma coleção usando índices. A partir de C# 13, o params parâmetro não está restrito a um array. Pode ser um tipo de coleção ou interface.
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
*/
}