Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
C# permite crear instancias de un objeto o colección y realizar asignaciones de miembros en una sola instrucción.
Inicializadores de objeto
Los inicializadores de objeto permiten asignar valores a los campos o propiedades accesibles de un objeto al crearlo. No es necesario invocar un constructor y, a continuación, usar instrucciones de asignación. La sintaxis del inicializador de objeto permite especificar argumentos para un constructor o omitir los argumentos y paréntesis. Para obtener instrucciones sobre el uso de inicializadores de objetos de forma coherente, consulte Uso de inicializadores de objeto (regla de estilo IDE0017) . En el ejemplo siguiente se muestra cómo usar un inicializador de objeto con un tipo con nombre, Caty cómo invocar el constructor sin parámetros. Tenga en cuenta el uso de propiedades implementadas automáticamente en la clase Cat. Para obtener más información, vea Propiedades implementadas automáticamente.
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 sintaxis del inicializador de objeto permite crear una instancia y asignar el objeto recién creado, con sus propiedades asignadas, a la variable de la asignación.
A partir de las propiedades anidadas de objetos, puede usar la sintaxis del inicializador de objetos sin la palabra clave new. Esta sintaxis, Property = { ... }, permite inicializar miembros de objetos anidados existentes, lo que resulta útil con propiedades de solo lectura. Para obtener más información, vea Inicializadores de objetos con propiedades con tipo de clase.
Los inicializadores de objeto pueden establecer indizadores, además de asignar campos y propiedades. Tenga en cuenta esta clase 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; }
}
}
Puede inicializar la matriz de identidades mediante el código siguiente:
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,
};
Puede usarse cualquier indizador accesible que contenga un establecedor accesible como una de las expresiones de un inicializador de objeto, independientemente del número o los tipos de argumentos. Los argumentos del índice forman el lado izquierdo de la asignación, mientras que el valor es el lado derecho de la expresión. Por ejemplo, los siguientes inicializadores siguientes son válidos si IndexersExample tiene los indizadores adecuados:
var thing = new IndexersExample
{
name = "object one",
[1] = '1',
[2] = '4',
[3] = '9',
Size = Math.PI,
['C',4] = "Middle C"
}
Para que el código anterior se compile, el tipo IndexersExample debe tener los siguientes miembros:
public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] { set { ... }; }
Inicializadores de objetos con tipos anónimos
Aunque puede usar inicializadores de objetos en cualquier contexto, son especialmente útiles en expresiones de Language-Integrated Query (LINQ). Las expresiones de consulta usan con frecuencia tipos anónimos, que solo se pueden inicializar mediante un inicializador de objeto, como se muestra en la siguiente declaración.
var pet = new { Age = 10, Name = "Fluffy" };
Mediante el uso de tipos anónimos, la select cláusula de una expresión de consulta LINQ puede transformar objetos de la secuencia original en objetos cuyo valor y forma difieren del original. Es posible que desee almacenar solo una parte de la información de cada objeto en una secuencia. En el ejemplo siguiente, suponga que un objeto del producto (p) contiene numerosos campos y métodos, y que solo le interesa crear una secuencia de objetos que contenga el nombre del producto y el precio por unidad.
var productInfos =
from p in products
select new { p.ProductName, p.UnitPrice };
Al ejecutar esta consulta, la productInfos variable contiene una secuencia de objetos a los que puede tener acceso en una foreach instrucción como se muestra en este ejemplo:
foreach(var p in productInfos){...}
Cada objeto del nuevo tipo anónimo tiene dos propiedades públicas que reciben los mismos nombres que las propiedades o los campos del objeto original. También puede cambiar el nombre de un campo al crear un tipo anónimo. En el ejemplo siguiente se cambia el nombre del UnitPrice campo a Price.
select new {p.ProductName, Price = p.UnitPrice};
Inicializadores de objeto con el required modificador
Utilice la required palabra clave para forzar a los autores de llamada a establecer el valor de una propiedad o campo mediante un inicializador de objeto. No es necesario establecer las propiedades necesarias como parámetros de constructor. El compilador garantiza que todos los autores de llamadas inicialicen esos 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();
Es una práctica habitual garantizar que el objeto se inicialice correctamente, especialmente cuando tienen varios campos o propiedades por administrar y no desea incluirlos todos en el constructor.
Inicializadores de objeto con el accesor init
Mediante un init descriptor de acceso, puede asegurarse de que el objeto no cambia después de la inicialización. Ayuda a restringir la configuración del valor de propiedad.
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";
Las propiedades de solo inicialización necesarias admiten estructuras inmutables, al tiempo que permiten la sintaxis natural para los usuarios del tipo.
Inicializadores de objeto con propiedades con tipo de clase
Al inicializar objetos con propiedades con tipos de clase, puede usar dos sintaxis diferentes:
-
Inicializador de objeto sin
newpalabra clave:Property = { ... } -
Inicializador de objeto con
newpalabra clave:Property = new() { ... }
Estas sintaxis se comportan de forma diferente. En el ejemplo siguiente se muestran ambos enfoques:
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
}
Principales diferencias
Sin
newpalabra clave (ClassB = { BI = 100003 }): esta sintaxis modifica la instancia existente de la propiedad que creó el constructor de objetos. Llama a los inicializadores de miembros del objeto existente.With
newkeyword (ClassB = new() { BI = 100003 }): esta sintaxis crea una nueva instancia y la asigna a la propiedad , reemplazando cualquier instancia existente.
El inicializador sin new reutiliza la instancia actual. En el ejemplo anterior, los valores de ClassB son: 100003 (nuevo valor asignado), true (mantenido de la inicialización de EmbeddedClassTypeA), BBBabc (valor predeterminado sin cambios de EmbeddedClassTypeB).
Inicializadores de objeto sin new para propiedades de solo lectura.
La sintaxis sin new es útil con propiedades de solo lectura. No puede asignar una nueva instancia, pero todavía puede inicializar los miembros de la instancia 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}");
}
}
Este enfoque permite inicializar objetos anidados incluso cuando la propiedad contenedora no tiene un establecedor.
Inicializadores de colección
Los inicializadores de colección permiten especificar uno o varios inicializadores de elementos al inicializar un tipo de colección que implementa IEnumerable y tiene un Add método con la firma adecuada como un método de instancia o un método de extensión. Los inicializadores de elemento pueden ser un valor, una expresión o un inicializador de objeto. Si se usa un inicializador de colección, no es necesario especificar varias llamadas; el compilador las agrega automáticamente. Para obtener instrucciones sobre cómo usar inicializadores de colección de forma coherente, consulte Uso de inicializadores de colección (regla de estilo IDE0028). Los inicializadores de colección también son útiles en las consultas LINQ.
En el ejemplo siguiente se muestran dos inicializadores de colección 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() };
El inicializador de colección siguiente usa inicializadores de objeto para inicializar los objetos de la clase Cat definida en un ejemplo anterior. Los inicializadores de objeto individuales se escriben entre llaves y se separan por comas.
List<Cat> cats =
[
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
];
Puede especificar null como elemento de un inicializador de colección si el método Add de la colección lo permite.
List<Cat?> moreCats = new List<Cat?>
{
new Cat{ Name = "Furrytail", Age=5 },
new Cat{ Name = "Peaches", Age=4 },
null
};
Puede usar un elemento de propagación para crear una lista que copie otras listas o listas.
List<Cat> allCats = [.. cats, .. moreCats];
E incluyen elementos adicionales junto con el uso de un elemento de propagación.
List<Cat> additionalCats = [.. cats, new Cat { Name = "Furrytail", Age = 5 }, .. moreCats];
Puede especificar elementos indexados si la colección admite indexación de lectura y escritura.
var numbers = new Dictionary<int, string>
{
[7] = "seven",
[9] = "nine",
[13] = "thirteen"
};
El ejemplo anterior genera código que llama a Item[TKey] para establecer los valores. También puede inicializar diccionarios y otros contenedores asociativos mediante la sintaxis siguiente. En lugar de la sintaxis del indexador, con paréntesis y una asignación, usa un objeto con varios valores:
var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
Este ejemplo de inicializador llama a Add(TKey, TValue) para agregar los tres elementos al diccionario. Estas dos maneras distintas de inicializar colecciones asociativas tienen un comportamiento ligeramente diferente debido a las llamadas a métodos que genera el compilador. Ambas variantes funcionan con la clase Dictionary. Es posible que otros tipos solo admitan una o la otra, en función de su API pública.
Argumentos de expresión de colección
A partir de C# 15, use el with(...) elemento como primer elemento de una expresión de colección para pasar argumentos al constructor de la colección. Con esta característica, puede especificar capacidad, comparadores u otros parámetros de constructor directamente dentro de la sintaxis de la expresión de colección:
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 obtener más información sobre los argumentos de expresión de colección, incluidos los tipos de destino admitidos y las restricciones, vea Argumentos de expresión de colección.
Inicializadores de objeto con inicialización de propiedad de colección de solo lectura
Algunas clases tienen propiedades de colección en las que la propiedad es de solo lectura, como la Cats propiedad de CatOwner en el caso siguiente:
public class CatOwner
{
public IList<Cat> Cats { get; } = new List<Cat>();
}
No se puede usar la sintaxis del inicializador de colección descrita anteriormente, ya que no se puede asignar una nueva lista a la propiedad:
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 }
}
};
Sin embargo, puede agregar nuevas entradas a Cats mediante la sintaxis de inicialización y omitir la creación de la lista (new List<Cat>), como se muestra en el ejemplo siguiente:
CatOwner owner = new CatOwner
{
Cats =
{
new Cat{ Name = "Sylvester", Age=8 },
new Cat{ Name = "Whiskers", Age=2 },
new Cat{ Name = "Sasha", Age=14 }
}
};
El conjunto de entradas que se van a agregar aparece entre llaves. El código anterior es idéntico a la escritura:
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 });
Ejemplos
En el ejemplo siguiente se combinan los conceptos de inicializadores de objeto y colección.
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.
}
En el ejemplo siguiente se muestra un objeto que implementa IEnumerable y contiene un método Add con varios parámetros. Usa un inicializador de colección con varios elementos por elemento en la lista que corresponden a la signatura del 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
*/
}
Los métodos Add pueden usar la palabra clave params para tomar un número variable de argumentos, como se muestra en el ejemplo siguiente. En este ejemplo además se muestra la implementación personalizada de un indizador para inicializar una colección mediante índices. A partir de C# 13, el params parámetro no está restringido a una matriz. Puede ser un tipo de colección o una interfaz.
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
*/
}