5

Note that this whole question assumes a nullable context as is the default in NET 6.

I want to write a method Read that takes in a nonnullable type (i.e. a struct or class) and outputs the nullable version of that struct or class. Something like this (obviously it wouldn't just return default but no need to complicate this):

public static T? Read<T>()
{
    return default; //should return null
}

This example method should return null, and indeed it does for classes, nullable classes, and nullable valuetypes but not valuetypes:

Console.WriteLine(null == Read<string>()); //true
Console.WriteLine(null == Read<string?>()); //true
Console.WriteLine(null == Read<int>()); //false
Console.WriteLine(null == Read<int?>()); //true

I want a method that returns true for all of these cases. I've tried a whole host of constraints on T. I feel like the notnull constraint should work but it doesn't.

I could just make two methods, one constrained to struct (i.e. value types) and one constrained to class (i.e. nonnullable reference types). The following works:

public static T? ReadValue<T>()  where T: struct
{
    return null; //does return null
}

public static T? ReadRef<T>()  where T: class
{
    return null; //does return null
}

But, as you can see, these two methods would be identical sans the constraint and different names (which is needed because they have the same signature). So it seems silly that I would have to make two different methods for this.

For context: I know that the jankiness of this situation is in part caused by nullable value types being represented by Nullable<T> under the hood while nullable classes are just kept track of by the compiler. I just want to know if there is a way to avoid this.

  • 2
    _"I just want to know if there is a way to avoid this."_ - there isn't, really. – Dai Jul 17 '22 at 04:50
  • 4
    What I've done with extension methods is put them in different classes so that the signatures don't conflict, and the compiler can choose the correct one. Since these are not extension methods, if you're okay with prefixing them with different class names, you'd get the same effect. But that looks like it has the same drawback as naming them differently. – madreflection Jul 17 '22 at 05:16
  • @madreflection That's a pretty clever idea, I might try that. – Tester Bill Jul 17 '22 at 05:19

1 Answers1

0

AFAIK this is impossible, due to the following:

  1. Overload resolution for calls without explicitly provided arguments (for calls with explicitly provided parameters of the generic type you could use approach similar to specified in answers to this question - see it yourself) - see the nice blog post by Jon Skeet.

  2. When generic type parameter T is not constrained neither to class nor to struct compiler will treat T? differently for value and reference types and for value types T? will be equal to T (see this answer).

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