Start Debugging

Solución: CS9035 "Required member 'X' must be set in the object initializer" en C#

CS9035 significa que un miembro marcado como required no se asignó. Asígnalo en el inicializador de objeto, o agrega un constructor con [SetsRequiredMembers] que asigne cada miembro required.

CS9035 se dispara en tiempo de compilación cuando creas una instancia de un tipo que tiene un miembro required, pero ese miembro no se asigna. El compilador quiere que cada campo o propiedad required se establezca antes de que el objeto salga de la construcción. Corrígelo de forma directa: agrega el miembro al inicializador de objeto, por ejemplo new Person { Name = "Ada" }. Si un constructor ya asigna el miembro, díselo al compilador poniendo [SetsRequiredMembers] en ese constructor para que deje de exigir el inicializador. Esto está verificado con C# 14 en .NET 11; el modificador required y este diagnóstico se comportan igual desde C# 11 en .NET 7.

El error en contexto

El mensaje completo nombra el miembro exacto que le falta al compilador:

error CS9035: Required member 'Person.Name' must be set in the object initializer or attribute constructor.

Verás un CS9035 por cada miembro required sin asignar, así que un tipo con tres propiedades required y un new Person() vacío produce tres errores a la vez. Es un diagnóstico de tiempo de compilación, no una excepción en tiempo de ejecución: la compilación falla, nada se ejecuta. Ese es todo el propósito de required: la comprobación pasó de ser un NullReferenceException que golpeabas en producción a un subrayado rojo que ves en el editor.

Por qué ocurre

El modificador required, introducido en C# 11, marca un campo o propiedad como obligatorio en el momento de la inicialización. Cuando cualquier expresión construye una nueva instancia del tipo, el compilador verifica que cada miembro required esté asignado, ya sea a través de un inicializador de objeto o de un constructor que promete establecerlos. Si no puede probarlo, emite CS9035.

La palabra clave es “probar”. El compilador no ejecuta tu código. Solo confía en dos cosas: un miembro que asignas directamente en el inicializador de objeto, y un constructor anotado explícitamente con [SetsRequiredMembers]. Un constructor que da la casualidad de que asigna el miembro en su cuerpo pero carece del atributo no cuenta para nada; el compilador no leerá el cuerpo para averiguarlo. Por eso el error persiste incluso cuando tu constructor claramente establece el valor: tienes que decírselo al compilador, no mostrárselo.

Tres situaciones producen el error:

Reproducción mínima

El tipo más pequeño que dispara CS9035:

// .NET 11, C# 14
public class Person
{
    public required string Name { get; init; }
    public required string Email { get; init; }
    public int Age { get; init; }   // not required, optional
}

Cada una de estas construcciones no compila:

// .NET 11, C# 14
var a = new Person();                        // CS9035 x2 (Name and Email)
var b = new Person { Name = "Ada" };         // CS9035 x1 (Email still missing)
var c = new Person { Age = 36 };             // CS9035 x2 (Name and Email)

Solo cuando ambos miembros required están presentes compila:

// .NET 11, C# 14 -- compiles
var ok = new Person { Name = "Ada", Email = "ada@example.com" };

Fíjate en que Age se omite y eso está bien. required es por miembro; los miembros opcionales siguen siendo opcionales. Al compilador solo le importa que los miembros que llevan el modificador required estén asignados.

La solución en detalle

Recorre estas opciones en orden. La primera es la respuesta en la gran mayoría de los casos; el resto cubre las situaciones donde un inicializador no es lo que quieres.

1. Asigna los miembros required en el inicializador de objeto

La solución prevista es establecer cada miembro required en el sitio de la llamada:

// .NET 11, C# 14
var person = new Person
{
    Name = "Ada Lovelace",
    Email = "ada@example.com",
    // Age is optional, omit it freely
};

Para esto sirve required: el contrato del tipo dice que estos campos son obligatorios, y el inicializador lo respeta. Si te encuentras repitiendo los mismos valores, es señal de que el tipo quiere un constructor, que es la siguiente solución.

2. Agrega un constructor anotado con [SetsRequiredMembers]

Cuando un constructor ya toma los valores y los asigna, decóralo con System.Diagnostics.CodeAnalysis.SetsRequiredMembers. Este atributo le asegura al compilador que el constructor inicializa cada miembro required, así que los llamadores ya no necesitan un inicializador de objeto:

// .NET 11, C# 14
using System.Diagnostics.CodeAnalysis;

public class Person
{
    public required string Name { get; init; }
    public required string Email { get; init; }
    public int Age { get; init; }

    [SetsRequiredMembers]
    public Person(string name, string email)
    {
        Name = name;
        Email = email;
    }
}

// now this compiles, no initializer needed
var person = new Person("Ada", "ada@example.com");

Un detalle delicado: [SetsRequiredMembers] es una aserción, no una garantía verificada. El compilador te toma la palabra y no comprueba que el constructor realmente asigne cada miembro required. Si agregas el atributo pero olvidas establecer Email en el cuerpo, no obtienes ningún CS9035 ni ninguna advertencia, solo un null donde prometiste un valor. Mantén el atributo honesto.

Si conservas un constructor sin parámetros junto al anotado, los llamadores de la versión sin parámetros aún necesitan el inicializador. El atributo solo exime al constructor específico donde reside.

3. Quita required si el miembro no es realmente obligatorio

Si el miembro tiene un valor por defecto razonable o es genuinamente opcional, no debería ser required en primer lugar. Quitar el modificador elimina la obligación:

// .NET 11, C# 14
public class Person
{
    public required string Name { get; init; }
    public string Email { get; init; } = "";   // was required, now optional with a default
}

Esta es la decisión correcta sorprendentemente a menudo. Recurre a required solo cuando no haya un valor por defecto razonable y construir sin el valor dejaría al objeto en un estado inválido. Darle un valor por defecto a una propiedad y quitar required es más limpio que forzar a cada sitio de llamada a pasar una cadena vacía.

4. Para records, usa un constructor posicional o con [SetsRequiredMembers]

Un record posicional genera una propiedad init por parámetro, pero esas no son required por defecto. Si agregas explícitamente una propiedad required a un record con un constructor primario, el constructor primario no la satisface automáticamente, y obtienes CS9035 cuando usas la forma posicional. Esto confunde a la gente porque parece que el constructor debería contar. Consulta la discusión de diseño en dotnet/csharplang #6780 para el razonamiento. La solución es establecer la propiedad en un inicializador encima de la llamada posicional, o agregar [SetsRequiredMembers] a un constructor que la asigne:

// .NET 11, C# 14
using System.Diagnostics.CodeAnalysis;

public record Product(string Sku)
{
    public required string Name { get; init; }

    [SetsRequiredMembers]
    public Product(string sku, string name) : this(sku) => Name = name;
}

// both work now
var withInit = new Product("A-100") { Name = "Widget" };
var withCtor = new Product("A-100", "Widget");

Si quieres que toda la construcción del record se aplique a través de los parámetros posicionales, prefiere propiedades init planas en lugar de propiedades required. Mezclar records posicionales y miembros required es una fuente de confusión; decide qué modelo usa el tipo. Para una visión más amplia de cuándo vale la pena un record, consulta record vs class vs struct en C#.

Puntos delicados y variantes

Un puñado de situaciones producen CS9035, o algo que se le parece, por razones que no son obvias a partir del mensaje:

El modelo mental que hay que retener: required mueve una regla de “debes proporcionar esto” de una esperanza en tiempo de ejecución a una garantía en tiempo de compilación, y CS9035 es esa garantía haciendo su trabajo. Cuando lo veas, la solución es siempre una de “proporciona el valor en el sitio de la llamada” o “dile al compilador que un constructor ya lo proporciona”. Decide cuál es cierta para el tipo, luego completa el inicializador o agrega [SetsRequiredMembers], y nunca recurras al atributo como una forma de silenciar el error sin establecer realmente el miembro.

Relacionados

Fuentes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver