6

I'm currently trying to apply the new C# 8.0 non-nullable reference type feature to existing code, and don't know how to fix the CS8603 warning in the following data deserialization method:

T ReadOptional<T>() where T : IEntity, new()
{
    if (ReadBoolean())
    {
        T instance = new T();
        instance.Read(this); // IEntity method
        return instance;
    }
    else
    {
        // CS8603 Possible null reference return.
        return default;
    }
}

As you can see, the method may return null (classes) / default (structs) if a preceeding boolean was false, otherwise it returns a T instance which can be anything implementing IEntity.

However, I cannot mark the return type as T?, because it would actually not return null if T is a struct, as correctly complained about by the compiler error CS8627:

// CS8627: A nullable type parameter must be known to be a value type or non-nullable
// reference type. Consider adding a 'class', 'struct', or type constraint.
T? ReadOptional<T>() where T : IEntity, new()
  • I cannot fix this by ensuring that T has a class constraint as I also expect struct instances to work with this method (returning default).
  • I cannot create an overload with T having a struct constraint as overloads cannot differ only by constraints.
  • I could create differently named methods, but that will break the library interface and by far too much code dependent on it.

Is there any syntax to fix the non-nullable warning without breaking the possibility of returning default instances for structs?

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Ray
  • 7,940
  • 7
  • 58
  • 90

1 Answers1

14

EDIT: There's a slightly improved solution posted here.

Browsing through a similar C# 8.0 question here, I filled a gap in my knowledge about nullable reference types: There's a null-forgiving operator ! which can fix the warning for me right here:

T ReadOptional<T>() where T : IEntity, new()
{
    if (ReadBoolean())
    {
        T instance = new T();
        instance.Read(this); // IEntity method
        return instance;
    }
    else
    {
        return default!; // <-- note the exclamation mark
    }
}
Ray
  • 7,940
  • 7
  • 58
  • 90
  • 4
    Wait, this doesn't seem right. Type T is marked as non-nulllable, so whatever it is, calling Readoptional must guarantee that it is not null, so this is deceiving the compiler, but more importantly, this can cause subtle bugs. Just saying this so everyone should be aware of this. – negyxo May 20 '19 at 07:26
  • @negyxo I'm sorry, there was a tiny copy-and-paste error in this answer (it is simplified source not used exactly like that in production) - the return type of course had to be `T?` as in the second sample in the answer. I added it now. – Ray May 20 '19 at 08:55
  • Doesn't your change then invalidate this *as* an answer to the question? If the question is "How can I return a non-nullable value from this method" and you're now just saying "Here's a nullable value from this method". The exclamation mark doesn't make the value non-null, it just tells the compiler to shut up, so if you use `ReadOptional`, and `ReadBoolean` returns `false`, you will still get `null` back. – Lasse V. Karlsen May 20 '19 at 09:04
  • 1
    To be fair, the question doesn't make much sense, unless `return default` actually means `return new T()`. – Lasse V. Karlsen May 20 '19 at 09:07
  • Sorry, I didn't mean to add confusion, but my intent was to address the potential issue with this solution. Though, I didn't put it right, Type T is not marked non-nullable or nullable, it is just generic type, which can be non-nullable type in which case this solution is not safe. As I see it, there is only one solution to this, and that's to pass the default value in a function, something like ReadOptional(T defaultValue). It's not nice, but it perserves type safety. – negyxo May 20 '19 at 22:39
  • @negyxo Yes, that's right. Attempting to return "`T?`" was only a syntactic try to get rid of the warning (as with marking class types with a question mark when returning `null`), not a semantic attempt to return a `Nullable`. As I cannot change the signature, I'm stuck with the solution of gulping down the warning with `default!`. – Ray May 20 '19 at 22:57
  • It is the same in Typescript. – ed22 Jan 22 '23 at 21:11