I'm trying to define a generic class that takes any type that can be set to null:
public abstract class BundledValue_Classes<T>
where T : class
{
private Tuple<T, object> _bundle= new Tuple<T, object>();
public T Value
{
get { return _bundle == null ? null : _bundle.Item1; }
set { _bundle = new Tuple<T, object>(value, obj); }
}
//...
This works fine. I also want to create a derived class that can take any primitive type (int, double, etc), and simply resolve that into the same as the above class, but where the primitive is wrapped in a Nullable
public abstract class BundledValue_Structs<T>
: BundledValue_Classes<T?>
where T : struct
{
//...
}
But for some reason, this doesn't work. I get the error: The type 'T?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'BundledValue_Classes<T>'
Is it clear what I'm trying to do here? I don't want to have to copy paste the contents of the whole BundledValue_Classes<T>
class just to handle the case of a primitive wrapped in a Nullable<T>
.
Perhaps there's some way to set the type constraint on BundledValue_Classes<T>
so that it will not only accept classes, but any type that can be assigned a null value (including Nullable<T>
types, which appear to be the only exception to the rule.)
Nullable<T>
is a struct, as per the documentation. It simply isn't a class. It's a value type, which looks a bit like this in terms of fields:
public struct Nullable<T>
{
private readonly T value;
private readonly bool hasValue;
}
The null
value for a Nullable<T>
type isn't a null reference, it's just a value where the hasValue
field is false.
Note that Nullable<T>
doesn't satisfy a condition for a constraint of where TFoo : struct
however, as that's actually a non-nullable value type constraint. Basically Nullable<T>
doesn't satisfy either the class
or the struct
constraint. There's no constraint which does only allow for nullable types (reference or Nullable<T>
).
As noted, the type Nullable<T>
is a struct which encapsulates a T
and a flag which indicates whether it is valid. Because the implementers of .NET didn't see any usefulness to being able to have a type which could encapsulate a thing of any thing and indicate whether it was valid, they decided that they didn't want to allow Nullable<Nullable<T>>
[a decision I dislike, by the way], and rather than come up with a special constraint just for the generic parameter in Nullable<T>
, they decided that even though Nullable<T>
is a struct, it shouldn't satisfy a struct
type constraint.
Personally, I'd have rather seen Nullable<T>
either simply be an "ordinary" struct with exposed fields of type bool
and unconstrained T
, which could be used by e.g. Dictionary.TryGetValue
to return a value and indicate whether it was valid (regardless of whether TValue
was a class, a non-nullable struct, or a Nullable<T>
), or else have it be a class containing a T
. The value of being able to say someNullable != null
as a shortcut is minuscule compared to the confusion and complications it causes compared with someNullable.HasValue
. Still, .NET is what it is. Personally, I would suggest generally avoiding nullable types, since one can't do much of anything with a nullable type without copying out the value first. Such types also have some weird quirks with regard to thread safety (e.g. code that receives a Nullable<T>
and observes that HasValue
cannot safely assume that GetValueOrDefault
will return Default(T)
, even if nothing can possibly have modified the Nullable<T>
since it was received).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With