6

Using nullable reference types in C#8.0, given this simplified code, where T can be any value type or reference type, and where null values are valid values of reference type T instances:

public class C<T>
{
    public T P { get; set; }
}

the compiler issues warning CS8618 "Non-nullable property 'P' is uninitialized. Consider declaring the property as nullable.". How can I satisfy the compiler and silence this warning (without suppressing CS8618)?

Is there an attribute, or a type parameter constraint that I can apply? In that case how?

The best I've come up with is

public T P { get; set; } = default!;

but I'm hoping for a way to do it without using the null-forgiving/"dammit" operator ('!').

Note: You cannot declare the type of the property as T? - CS8627. Besides, that would imply that any value type returned would be Nullable<T>, which is not what is intended. (Read more about this e.g. here.)


Edit: I notice this also works:

public class C<T>
{
    public C()
    {
        P = default;
    }
    [AllowNull]
    public T P { get; set; }
}

However I would have prefered to use an auto-property initializer.

Ulf Åkerstedt
  • 3,086
  • 4
  • 26
  • 28
  • First you need to decide what *you* want to do. What do *you* want that value to contain until it's set for the first time? Some default instance? Should it be read-only? Is there any chance that `get;` would return a `null`? Perhaps you should use an `Option`-style type that can either have a value or be `None` ? – Panagiotis Kanavos Dec 06 '19 at 14:48
  • Each option can be implemented in a different way, through the constructor, nullability attributes or a custom Option struct [like this one](https://stackoverflow.com/questions/58648767/how-to-deal-with-optional-arguments-when-wanting-to-enable-nullable-reference-ty/58663401#58663401) – Panagiotis Kanavos Dec 06 '19 at 14:49
  • I'm expecting the property value to be default. I.e. for ```int``` it should be '0', and for ```object``` it should be 'null' just as if the class had been defined with either of these types instead of the generic type parameter. – Ulf Åkerstedt Dec 06 '19 at 14:54
  • And what is that `default` ? It can't be null and still be a non-nullable reference. That's the whole point of the feature. If you want it to be null, set it to `T?`. If you really want to take advantage of non-nullable types though, modify your design so it *doesn't* need nulls – Panagiotis Kanavos Dec 06 '19 at 14:56
  • I would suggest using a `class` constraint. Very rarely does a generic type *need* to support both classes and structs. Since nullability works differently for each of them, you run into this issue right away. Otherwise, maybe try using `[AllowNull]` with `default!`. – Dave Cousineau Dec 08 '19 at 11:24

2 Answers2

1

Basically, class and struct nullability are simply incompatible and cannot be combined.

Your options include:

  • Keep your class completely generic; so, no differentiating within your class between T and T?. This does still allow for C<int> and C<int?>, just not T?.

  • Constrain either to class or struct, depending on your requirements.

  • Write two separate classes, one for structs one for reference types.

It's not that bad to write two classes if they're simple enough. You might be able to push the non-generic parts into a base class to avoid too much repetition.

Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
0

When you instantiate your class the value of property P will be null and this is the reason why you see the warning.

You need to ensure the value is not null when you instantiate the class and the only way is to define a constructor and provide value for P. As T is a generic type you have two options:

  1. Pass the value as the constructor parameter or
  2. Constrain T to be able to create new instances
// Option #1
public class C<T>
{
   C(T p)
   {
       P = p
   }

   public T P { get; set; }
}

// option #2:
public class C<T> where T : new()
{
   C()
   {
       P = new T()
   }

   public T P { get; set; }
}

Aik
  • 3,528
  • 3
  • 19
  • 20
  • 4
    I was going to write an answer similar to this at first, but I think the issue is that the OP *does* actually want the property to be nullable. The problem is that you can't mark it nullable with `T?` without either a `class` or `struct` constraint, since their nullabilities are not compatible. – Dave Cousineau Dec 09 '19 at 13:09
  • yes, if the property is supposed to be nullable, `T?` is the right solution – Aik Dec 09 '19 at 13:27