C# 11 required member false positive

394 views Asked by At

The code below can not compile and reports a CS9035 error. Required member 'A.FieldA' must be set in the object initializer or attribute constructor. This seems like a false positive. The required field is set. Is there any way to work around this? I don't want the users of record B to set the field.

public record A
{
    public A(string value)
    {
        FieldA = value;
    }
    required public string FieldA { get; init; }
}
public record B() : A("field a");

public static class Test 
{
    public static void test() 
    {
        var b = new B(); //Error    CS9035  Required member 'A.FieldA' must be set in the object initializer or attribute constructor.
        //var b = new B()  { FieldA = "field a" };
    }
}
3

There are 3 answers

2
Etienne de Martel On BEST ANSWER

Let's take a look at the documentation:

The required modifier indicates that the field or property it's applied to must be initialized by an object initializer.

So it's only for object initializers. What you do in your constructors does not matter. But then, if you're initializing it in the constructor, you probably want a read only property, not an init only one.

If you want to do it that way, you'll have to set the SetsRequiredMembers attribute to your constructor, but the documentation warns against that:

The SetsRequiredMembers disables the compiler's checks that all required members are initialized when an object is created. Use it with caution.

0
Sweeper On

You can do this if you add SetsRequiredMembers to both A and B's constructors.

[SetsRequiredMembers]
public A(string value)
{
    FieldA = value;
}
[method: SetsRequiredMembers]
public record B() : A("field a");

Note that no checks are performed to make sure that you actually set all required members in the constructors. The compiler just trusts you.

Also, it is not possible to just want some required properties to be set in the constructor. See also this question, and this discussion about adding ways to say "this constructor only initialises the required members X, Y and Z".

The main argument against this is:

Our theory is that most people using init methods and constructor parameters aren't going to be using required members in the first place. I'm not particularly convinced that there will be a large number of users with required members that also want to have multiple constructors that set subsets of their members, much less a large number of users with enough of these constructors that they want to factor them into helper methods.

0
tmaj On

This is because the requirement is different to "it has to be set":

In the Required Members feature specification we can read:

All constructors in a type with required members, or whose base type specifies required members, must have those members set by a consumer when that constructor is called.

(emphasis mine)

The spec then follows to describe SetsRequiredMembersAttribute which removes the requirement.

Here's a setup that eliminates CS9035:

[method: SetsRequiredMembers]
public record A(string fieldA)
{
    required public string FieldA { get; init; } = fieldA;
}

[method: SetsRequiredMembers]
public record B(): A(fieldA: "field a");

(or)

public record A
{
    [SetsRequiredMembers]
    public A(string fieldA)
    {
        FieldA = fieldA;
    }
    required public string FieldA { get; init; }
}