c# - How to declare a Nullable Property with Non-Nullable init - Stack Overflow

admin2025-04-28  4

I have a nullable property like so:

string? MyString
{
    get { ... }
    init
    {
        if (value is null)
            return;
        ...
    }
}

I don't like the guard against null's in the init, and would prefer that the compiler enforce the condition:

string? MyString
{
    get { ... }
}

string MyString
{
    init { ... }
}

The compiler doesn't like this. The closest I can get is with the DisallowNullAttribute:

[DisallowNull]
string? MyString
{
    get { ... }
    init
    {
        if (value is null)
            return;
        ...
    }
}

This only provides a warning, so the guard is still necessary.

Is there a better way to achieve what I want?

I have a nullable property like so:

string? MyString
{
    get { ... }
    init
    {
        if (value is null)
            return;
        ...
    }
}

I don't like the guard against null's in the init, and would prefer that the compiler enforce the condition:

string? MyString
{
    get { ... }
}

string MyString
{
    init { ... }
}

The compiler doesn't like this. The closest I can get is with the DisallowNullAttribute:

[DisallowNull]
string? MyString
{
    get { ... }
    init
    {
        if (value is null)
            return;
        ...
    }
}

This only provides a warning, so the guard is still necessary.

Is there a better way to achieve what I want?

Share Improve this question asked Jan 9 at 18:06 onbebenonbeben 313 bronze badges 6
  • 4 What goal (semantically) are you trying to achieve? If you want to enforce the requirement to init property with a non-null value, then why would you want to declare the property itself as nullable? In what case do you expect it to be null? – Serg Commented Jan 9 at 18:14
  • 1 And, anyway, the nullability checks will always generate just a warnings at compile time. But you can configure to treat warnings as errors in the project settings (but it will treat all the warnings as errors, not only nullability-related warnings). – Serg Commented Jan 9 at 18:17
  • 1 Semantically, what I'm trying to achieve is an optional property that can only be set at initialization, but if it is set must have a value (therefore the initializer does not have the required keyword). The purpose is to ensure that users of the property do not accidentally assign the property a null value and then think that the property has been set. This ensures stronger compile-time guarantees and reduces errors. – onbeben Commented Jan 9 at 18:30
  • So, the property itself has the null value by default (and user can check that value via the get-ter), but you want to prevent user from explicitly initializing this property with null value? And how about the cloning/mapping scenarios when we just transfer data from the one object to the new one property-by-property? Is it ok that we will need to handle this specific property in very specific way? – Serg Commented Jan 9 at 18:37
  • 1 By the way, can you please add the content of your previous comment directly into the question? – Serg Commented Jan 9 at 18:38
 |  Show 1 more comment

1 Answer 1

Reset to default 4

The closest I can get is with the DisallowNullAttribute: This only provides a warning, so the guard is still necessary.

In terms of compiler's null-state static analysis DisallowNullAttribute is the answer (as the Preconditions: AllowNull and DisallowNull section of the docs states).

To "fix" the "only warnings" problem you can use several options:

  1. Enable the TreatWarningsAsErrors option

    <PropertyGroup>
        <TreatWarningsAsErrors/>
    </PropertyGroup>    
    
  2. Use <WarningsAsErrors>Nullable</WarningsAsErrors>:

    <PropertyGroup>
        <WarningsAsErrors>Nullable</WarningsAsErrors>
    </PropertyGroup>    
    
  3. Use concrete diagnostic (full list) for more granular control for example via editor config. For example:

    [*.cs]
    dotnet_diagnostic.CS8601.severity = error
    dotnet_diagnostic.CS8602.severity = error
    dotnet_diagnostic.CS8625.severity = error
    

Note that those are compiler checks which can still be "overriden" via the null-forgiving operator or by (temporary) disabling nullable context (if we don't dive into other "more complex" options like deserialization or reflection):

var myClass = new MyClass
{
    MyString = null! // NO COMPILER ERROR DESPITE SETTINGS!
};

class MyClass
{
    [DisallowNull]
    public string? MyString // sample implementation to ignore
    {
        get => "1";
        init => Console.WriteLine(value);
    }
}

So if you want the most foolproof code then you need to keep the check.

转载请注明原文地址:http://anycun.com/QandA/1745781833a91213.html