C
C#•3d ago
Pherenetic

NRT: How to correctly annotate my generic-class when inheriting from another generic class?

Hello. I'm trying to correctly annotate nullable reference types (NRT) to a generic class derived from another generic class. Here is some simplified code:
// Some dependency
interface IOther {
K? Get<K>();
}

// The derived class which I want to annotate correctly
public class MyDictionary<T,K> : Dictionary<T, K> where T: notnull {
public void Foo(T key) {
IOther x = null!;
Add(key, x.Get<K>()); // This triggers a CS8604
}
}

// Usage of the class
var tmp = new MyDictionary<Guid, string>();
tmp.Add(Guid.NewGuid(), null);
tmp.Add(Guid.NewGuid(), "null");
// Some dependency
interface IOther {
K? Get<K>();
}

// The derived class which I want to annotate correctly
public class MyDictionary<T,K> : Dictionary<T, K> where T: notnull {
public void Foo(T key) {
IOther x = null!;
Add(key, x.Get<K>()); // This triggers a CS8604
}
}

// Usage of the class
var tmp = new MyDictionary<Guid, string>();
tmp.Add(Guid.NewGuid(), null);
tmp.Add(Guid.NewGuid(), "null");
The shown implementation triggers the CS8604 as commented which I understand. But how do I tell the compiler: I don't know if the user will use a nullable type or not for K? When I change Dictionary<T, K> to Dictionary<T, K?> intellisense says that even my instance tmp can have nulls added but my type parameter clearly states that I don't want that to be allowed. So how do I correctly annotate this kind of scenario (I have a couple more like that)? Thanks in advance
31 Replies
ero
ero•3d ago
what do you mean by
my type parameter clearly states that I don't want that to be allowed
?
Aaron
Aaron•3d ago
the problem is that IOther.Get always returns a nullable value
Pherenetic
PhereneticOP•3d ago
MyDictionary<Guid, string> <- No ? on string.
Aaron
Aaron•3d ago
if I do MyDictionary<string, object>, then IOther.Get returns object? then you try to call Add, which takes K, which is object (not nullable) aka you're trying to pass K? as K
Pherenetic
PhereneticOP•3d ago
Exactly
Aaron
Aaron•3d ago
what exactly do you want the compiler to do about that you're doing something you explicitly said you wouldn't do like, it doesn't matter what the actual user of your class does, you are allowing them to pass in object as K your type parameter doesn't say at all that you don't want nulls you have to pick one, either make Get not return nulls or the dictionary has to be allowed to contain nulls you can't have both
Pherenetic
PhereneticOP•3d ago
I can't decide whether K will be nullable or not
ero
ero•3d ago
it depdends on the impl of the dependency really. if it can return a null value regardless of whether K is nullable or not, then there's nothing you can do about it. you have to assume that Get<K> will return null in some cases. that's what the dependency says.
Aaron
Aaron•3d ago
. you are the one picking this it has nothing to do with the users of the class
ero
ero•3d ago
it's a dependency, they're not the one picking it
Aaron
Aaron•3d ago
they can always throw on nulls or make a new interface
Pherenetic
PhereneticOP•3d ago
My problem/question is: How do I implement MyDictionary<T, K> : Dictionary<T, K> where I can leverage NRT and it's up to an actual consumer of that type if he wants to allow null or not.
ero
ero•3d ago
it's not up to you. the dependency says it can return null
Aaron
Aaron•3d ago
what do you do when IOther returns null when the user said it can't contain nulls
Pherenetic
PhereneticOP•3d ago
That's my problem xD I can't just check against null...
Aaron
Aaron•3d ago
I mean, that's not something the compiler can help you with at all it's a design choice yes you can? x.Get<K>() is null works just fine
Pherenetic
PhereneticOP•3d ago
That's true. But I can't decide whether the used K from the consumer allows null or not.
ero
ero•3d ago
that doesn't matter here at all even when K in MyDictionary<T, K> is not nullable, the return value of Get<K> is they're unrelated
Aaron
Aaron•3d ago
yes, it has to purely be a design consideration of your implementation I believe they essentially want
var k = x.Get<K>();
if (K is not nullable && k is null)
throw new Whatever();
var k = x.Get<K>();
if (K is not nullable && k is null)
throw new Whatever();
which yeah, you can't do that information isn't available to the implementation, NRTs are purely compile time
Pherenetic
PhereneticOP•3d ago
GitHub
runtime/src/libraries/System.Private.CoreLib/src/System/Collections...
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps. - dotnet/runtime
Aaron
Aaron•3d ago
that throw helper is for value types it only works when assigning object to T where T is a non-nullable value type aka it doesn't work for you I meant it when I said this information is not available at runtime
Pherenetic
PhereneticOP•3d ago
Yeah I know it's only compile time 🤔 So I have to either try change my dependency or suppress the warning with ! and hope for the best 🥴
ero
ero•3d ago
if you can control the dependency, then you should change the implementation, yes Get<K> should only return K, and a TryGet method would be nice to have as well i assume K? Get<K> means a potential failure in getting that info should be conveyed through other means
Pherenetic
PhereneticOP•3d ago
No it really means K could be null here.
ero
ero•3d ago
i'm not really sure what you mean. Get<K> can return null even when K may not be nullable? that's what it currently means
Pherenetic
PhereneticOP•3d ago
💡 let me check And that's exactly what it does. Returning null even if K would be string 🤔
ero
ero•3d ago
in what circumstance? and is there no TryGet method?
Pherenetic
PhereneticOP•3d ago
There is no TryGet and if it fails to get & convert that value.
ero
ero•3d ago
that's bad design by that api then a failure like that should throw, not return null
Pherenetic
PhereneticOP•3d ago
I agree Thank you for your answers. That was very enlightening.
ero
ero•3d ago
i can't really think of a good way to handle this in that case. you would need to provide your own implementation of IOther
Want results from more Discord servers?
Add your server