null assignment to T? where T : INumber<T> - c#

I want to create a Blazor(wasm) InputNumber<T> component where T : INumber<T>.
Inside this component I have a simple function to set the Value:
this compiles good
void SetValue(T? value)
{
if (value is null)
{
....
}
....
}
but when I try to call SetValue(null) the compiler says:
CS1503: "cannot convert from <null> to T?"
I was expecting that if the method parameter is T? then I should be able to pass null to it.
e.g.
void SetDecimal(decimal? value)
{
if (value is null)
{
...
}
}
This of course works: SetDecimal(null);
What am I missing?

The problem here arises from the difference between how nullable value and nullable reference types work - former are actually represented as separate type Nullable<T> while latter are not and represented by the runtime as metadata. Which results in a bit non-intuitive generic handling of T? depended on the constraints (see more in the docs or here).
One of the possible workarounds is to limit T to struct (if it is usable for you):
void SetValue<T>(T? value) where T: struct
{
if (value is null)
{
}
}

Related

Casting object? to a generic type that may or may not be nullable

I'm trying to convert a project to use nullable reference types, but I'm running into an issue. In my project, I have a place where I get an object? that needs to be cast to a generic type T before adding it to a collection. The type T could be anything; a nullable reference type, a non-nullable reference type, or a value type. This isn't known at compile time.
So, let's say I have the following code (toy example):
static T Convert<T>(object? value)
{
return (T)value;
}
This causes the compiler to complain that value may be null, and that the return of the function may be null. That's fair enough, since if T is non-nullable and value is null, this wouldn't be allowed. I thought maybe this would work:
static T Convert<T>(object? value)
{
if (value == null)
return default;
else
return (T)value;
}
But this has the same problem: if T is a non-nullable reference type, default is still null, which still violates the constraint.
Making the function return T? is not a solution, because in the case of value types, I don't want to use Nullable<T>.
I thought about throwing an exception if value is null, but I want to allow null if T is nullable. So I'd only want to throw that if T is non-nullable, and that kind of generic specialization doesn't seem possible in C#.
The context here is that I'm using a TypeConverter, and unfortunately the result of conversion is allowed to return null.
Is there a good way to handle this situation?
If you use C# 9.0 or higher, you can use return type of T? without need to resort to Nullable<T>. For generics in non-nullable context there is special set of rules, detailed here.
If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.
You can check GetType() of T with simple console application. If T is int, return type will be System.Int32, not Nullable<System.Int32>.
#nullable enable
using System;
public class Program
{
public static void Main()
{
var result = Convert<int>(null);
Console.WriteLine(result); // Prints: 0
Console.WriteLine(result.GetType().FullName); // Prints: System.Int32
}
static T? Convert<T>(object? value)
{
if (value is null)
return default(T);
return (T)value;
}
}
C# Playground example here.

C# 8.0 nullable and generics [duplicate]

I'm playing around a bit with the new C# 8 nullable reference types feature, and while refactoring my code I came upon this (simplified) method:
public T Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default;
}
Now, this gives a warning
Possible null reference return
which is logical, since default(T) will give null for all reference types. At first I thought I would change it to the following:
public T? Get<T>(string key)
But this cannot be done. It says I either have to add a generic constraint where T : class or where T : struct. But that is not an option, as it can be both (I can store an int or int? or an instance of FooBar or whatever in the cache).
I also read about a supposed new generic constraint where class? but that did not seem to work.
The only simple solution I can think of is changing the return statement using a null forgiving operator:
return wrapper.HasValue ? Deserialize<T>(wrapper) : default!;
But that feels wrong, since it can definitely be null, so I'm basically lying to the compiler here..
How can I fix this? Am I missing something utterly obvious here?
You were very close. Just write your method like this:
[return: MaybeNull]
public T Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default!;
}
You have to use the default! to get rid of the warning. But you can tell the compiler with [return: MaybeNull] that it should check for null even if it's a non-nullable type.
In that case, the dev may get a warning (depends on flow analytics) if he uses your method and does not check for null.
For further info, see Microsoft documentation: Specify post-conditions: MaybeNull and NotNull
I think default! is the best you can do at this point.
The reason why public T? Get<T>(string key) doesn't work is because nullable reference types are very different from nullable value types.
Nullable reference types is purely a compile time thing. The little question marks and exclamation marks are only used by the compiler to check for possible nulls. To the eyes of the runtime, string? and string are exactly the same.
Nullable value types on the other hand, is syntactic sugar for Nullable<T>. When the compiler compiles your method, it needs to decide the return type of your method. If T is a reference type, your method would have return type T. If T is a value type, your method would have a return type of Nullable<T>. But the compiler don't know how to handle it when T can be both. It certainly can't say "the return type is T if T is a reference type, and it is Nullable<T> if T is a reference type." because the CLR wouldn't understand that. A method is supposed to only have one return type.
In other words, by saying that you want to return T? is like saying you want to return T when T is a reference type, and return Nullable<T> when T is a value type. That doesn't sound like a valid return type for a method, does it?
As a really bad workaround, you could declare two methods with different names - one has T constrained to value types, and the other has T constrained to reference types:
public T? Get<T>(string key) where T : class
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : null;
}
public T? GetStruct<T>(string key) where T : struct
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? (T?)Deserialize<T>(wrapper) : null;
}
In C# 9 you are able to express nullability of unconstrained generics more naturally:
public T? Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default;
}
Note there's no ! operator on the default expression. The only change from your original example is the addition of ? to the T return type.
In addition to Drew's answer about C# 9
Having T? Get<T>(string key) we still need to distinguish nullable ref types and nullable value types in the calling code:
SomeClass? c = Get<SomeClass?>("key"); // return type is SomeClass?
SomeClass? c2 = Get<SomeClass>("key"); // return type is SomeClass?
int? i = Get<int?>("key"); // return type is int?
int i2 = Get<int>("key"); // return type is int

Implicit conversion from null

Looking Zoran Horvats courses at PluralSight, I'm currently implementing a Maybe type, a bit like Zoran has on its GitHub account: https://github.com/zoran-horvat/option
Generally, a Maybe is a wrapper around objects, which are either set or have a null value, avoiding null reference exceptions.
To make the code a bit shorter, I would like to use implicit conversion to map the values / nulls to their corresponding maybe types. Here an example of my code:
public void Hook(Maybe<Action<Keys>> onKeyDown, Maybe<Action<Keys>> onKeyUp)
{
_keyDownCallback = onKeyDown;
_keyUpCallback = onKeyUp;
_hookService.Hook(HookType.KeyBoardLowLevel, OnHookReceived);
}
As you can see, you can hook and pass two optional callbacks, one for keyDown and one for keyUp. I would like to pass code like this:
nativeKeyboardHookService.Hook(new Action<Keys>(OnNativeKeyDown), null);
The implicit conversion on the Maybe is currently implemented like this:
public static implicit operator Maybe<T>(T value)
{
return ToMaybe(value);
}
public static implicit operator T(Maybe<T> maybe)
{
return ToT(maybe);
}
public static Maybe<T> ToMaybe(T value)
{
if (value == null)
{
return new None<T>();
}
return new Some<T>(value);
}
public static T ToT(Maybe<T> maybe)
{
return maybe.Evaluate(
value => value,
() => default(T));
}
My question: It works fine, if I pass an actual object, mapping it to an Maybe, but if I pass NULL, I still get a NULL object, not a None object. Am I doing here something wrong or is it just not possible? I didn't find any further information regarding such a conversion.
When you pass null to Hook() that's literally all you are doing because your implicit casts aren't being invoked at all. That's because null is a valid value for a reference type, and thus no need to cast.
You can't change Maybe to a struct if you want to keep Some and None because then these would have to be structs too, which means you run into the issue that you can't inherit structs.
You can't implement a common IMaybe<T> interface either because interfaces can't be used with casts.
What I recommend is keep your behavior as is, but don't use null. Instead of passing null, pass something else like Maybe<T>.None:
class Maybe<T>
{
public static Maybe<T> None { get; } = new None<T>();
}
void Hook(..., Maybe<T>.None) { ... }
Or None<T>.Instance:
class None<T>
{
public static None<T> Instance{ get; } = new None<T>();
}
void Hook(..., None<T>.Instance) { ... }
This has the advantage of being more readable and explicit.
Your Maybe<T> is still a reference type, so null is a valid value for it:
Maybe<string> foo = null;
If you want to prevent that, you will need to make it a value type, for example something like this:
public struct Maybe<T>
{
public T Value { get; }
public bool IsEmpty => Value == null;
public Maybe(T value)
{
Value = value;
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
}
Then you can pass null to a method expecting a Maybe<T> and it will properly construct an empty Maybe<T> object.
But note that it being a value type, this now means that it is copied on every method call, so it has a different behavior to a reference type implementation.
In the end, you cannot really implement this nicely in C# simply because there is the null reference in the language. It’s only with C# 8’s nullable reference types that you will be able to prevent nulls altogether.

How can I check if a value can be cast to a generic type?

I have a method wrapping some external API call which often returns null. When it does, I want to return a default value. The method looks like this
public static T GetValue<T>(int input)
{
object value = ExternalGetValue(input);
return value != null ? (T)value : default(T)
}
The problem is that (T)value might throw an invalid cast exception. So I thought I would change it to
var value = ExternalGetValue(input) as Nullable<T>;
but this requires where T : struct, and I want to allow reference types as well.
Then I tried adding an overload which would handle both.
public static T GetValue<T>(int input) where T : struct { ... }
public static T GetValue<T>(int input) where T : class { ... }
but I found you can't overload based on constraints.
I realize I can have two methods with different names, one for nullable types and one for nonnullable types, but I'd rather not do that.
Is there a good way to check if I can cast to T without using as? Or can I use as and have a single method which works for all types?
You can use is:
return value is T ? (T)value : default(T);
(Note that value is T will return false if value is null.)

"Error CS0118: 'field' used as a 'type'" with a Type field

I'm writing a class that holds a variable of any type. To do so, I store both the variable (as an object reference) and its Type. When I try to cast the object back to the correct type, though, I get error CS0118, because I'm using a field (which is of type Type) as a type.
Here is my class:
public class Node
{
Type m_oType = null;
public Type Type
{
get { return m_oType; }
set { m_oType = value; }
}
object m_oValue = null;
public object Value
{
get { return m_oValue; }
set
{
if (m_oValue == null)
{
if (value is m_oType) // ERROR CS0118
{
m_oValue = value;
}
}
}
}
}
I've tried to search online for a way to do this (i.e., using cast operators, as and is), but I keep getting the same basic tutorials about casting variables. Can someone give me a pointer as to how I can achieve this? Thanks.
I suspect you want something like this (but read on):
if (m_oType.IsAssignableFrom(value.GetType()))
Note that we're calling it on m_oType, not passing m_oType to it. From the docs on IsAssignableFrom's return value, where c is the parameter:
true if c and the current Type represent the same type, or if the current Type is in the inheritance hierarchy of c, or if the current Type is an interface that c implements, or if c is a generic type parameter and the current Type represents one of the constraints of c.
For example, typeof(object).IsAssignableFrom(typeof(string)) returns true because object is in the inheritance hierarchy of string.
EDIT: As noted, that will break if either m_oType is null or value is null. We can get around value being null easily enough, but it's not clear what you'd expect it to do if m_oType is null. Perhaps you should prevent that in the setter for the Type property (and the constructor)? Then use either:
// This will always store a null
if (value == null || m_oType.IsAssignableFrom(value.GetType()))
// This will never store a null
if (value != null && m_oType.IsAssignableFrom(value.GetType()))
Something like
if (m_oType.IsAssignableFrom(value.GetType()))
But I'm pretty sure you should have a look at Generics.
public class Node<T>
{
T m_oValue = null;
public T Value
{
get { return m_oValue; }
set
{
if (m_oValue == null)
{
m_oValue = value;
}
}
}
}

Categories

Resources