3

With the following code

abstract class Base
{
    protected abstract T? GetValue<T>([CallerMemberName] string propertyName = "");
}

class Derived : Base
{
    protected override T GetValue<T>([CallerMemberName] string propertyName = "")
    {
        return default;
    }
}

the compiler tells me at return default; that I have a CS8603 "Possible null reference return" which is true. However if I append a ? to the return type of that method so that reads (like the abstract method) protected override T? GetValue<T>([CallerMemberName] string propertyName = "") the compiler tells me

  • CS0508 'Derived.GetValue(string)': return type must be 'T' to match overridden member 'Base.GetValue(string)'.
  • CS0453 The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'.

How do I tell the compiler my intent that GetValue<T> might return a null reference and not that the return type of that method should be Nullable<T>?

David
  • 2,426
  • 3
  • 24
  • 36
  • Can `T` be limited to only classes or you need to support both reference and value types? – Guru Stron Jan 11 '22 at 17:42
  • `T` should be allowed to be anything - both reference and value types. – David Jan 11 '22 at 17:51
  • I would have expected that you could perform a `default(T) is null` check and that would satisfy the compiler, but it turns out that it is not sufficient. According to this post https://stackoverflow.com/a/59328672/1165998, it doesn't seem like this is possible. – David L Jan 11 '22 at 18:11

2 Answers2

4

The solution was to place the [return: MaybeNull] attribute on the method override as follows:

[return: MaybeNull]
protected override T GetValue<T>([CallerMemberName] string propertyName = "")
{
  return default;
}

The compiler now no more lists warning CS8603.

David
  • 2,426
  • 3
  • 24
  • 36
1

You can use default generic constraint to make it compile:

abstract class Base
{
    protected abstract T? GetValue<T>([CallerMemberName] string propertyName = "");
}

class Derived : Base
{
    protected override T? GetValue<T>([CallerMemberName] string propertyName = "") where T : default
    {
        return default;
    }
}

But for value types default(T) will be returned not default(Nullable<T>) so it needs to be invoked with nullable value type for type parameter to achieve desired behaviour.

You can try to workaround by having 2 methods with different type constraints and dummy T? == null parameter:

abstract class Base
{
    public abstract T? GetValue<T>([CallerMemberName] string propertyName = "", T? _ = null) where T : struct;
    public abstract T? GetValue<T>([CallerMemberName] string propertyName = "", T? _ = null) where T : class;
}

class Derived : Base
{
    public override T? GetValue<T>([CallerMemberName] string propertyName = "", T? _ = null) where T : struct
    {
        return default;
    }
    public override T? GetValue<T>([CallerMemberName] string propertyName = "", T? _ = null) where T : class
    {
        return default;
    }
}

But it's up to you to decide how usable/ugly this is)

Guru Stron
  • 102,774
  • 10
  • 95
  • 132