Why does:
string s = "";
bool sCanBeNull = (s is Nullable);
s = null;
sCanBeNull equate to false?
I'm writing a code generator and need to ensure every type passed to it is nullable, if it isn't already.
//Get the underlying type:
var type = field.FieldValueType;
//Now make sure type is nullable:
if (type.IsValueType)
{
var nullableType = typeof (Nullable<>).MakeGenericType(type);
return nullableType.FullName;
}
else
{
return type.FullName;
}
Do I need to have to explicitly check for a string or am I missing something?
is tells you whether a value is of a particular type, or one derived from that particular type.
Nullable is a generic struct that allows for nullable versions of non-nullable values.
string is not a Nullable
To tell if a type can have null values use the fact that for all such types the default value is null, while for all other types it is not:
default(string) == null; // true
string is a reference type so it is not Nullable as that is reserved for value types.
In fact:
var nullable = new Nullable<string>();
Gives a compile time error.
The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
The rational behind System.Nullable is to be able to represent undefined Value Types using the null keyword. It does not mean that you can check whether some variable can be set to null using someVar is Nullable.
If at compile-time you cannot know beforehand whether some variable would be a value-type or a reference-type, you can use:
!someVar.GetType().IsValueType
But usually a generic argument would be a better approach.
Nullable is just a normal class like MyClass or System.String (Nullable<T> is struct though).
So if you type:
class _Nullable {}
struct _Nullable<T> {}
class Program
{
static void Main()
{
string a = "";
Console.Write(a is _Nullable);
}
}
You wouldn't be surprised if it returns false right ?
If you want to check if something is nullable or not you can use if(!(a is ValueType))
string a = "";
Console.Write("a is nullable = {0}", !(a is ValueType));
Output : a is nullable = true
Related
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.
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
First: this is not a duplicate of How to check if an object is nullable?. Or, at least, there was no useful answer provided to that question, and the author's further elaboration actually asked how to determine whether a given type (eg. as returned from MethodInfo.ReturnType) is nullable.
However, that is easy. The hard thing is to determine whether a runtime object whose type is unknown at compile time is of a nullable type. Consider:
public void Foo(object obj)
{
var bar = IsNullable(obj);
}
private bool IsNullable(object obj)
{
var type = obj.GetType();
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
// Alternatively something like:
// return !(Nullable.GetUnderlyingType(type) != null);
}
This does not work as intended, because the GetType() call results in a boxing operation (https://msdn.microsoft.com/en-us/library/ms366789.aspx), and will return the underlying value type, rather than the nullable type. Thus IsNullable() will always return false.
Now, the following trick uses type parameter inference to get at the (unboxed) type:
private bool IsNullable<T>(T obj)
{
var type = typeof(T);
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
This seems initially promising. However, type parameter inference only works when the type of the object is known at compile time. So:
public void Foo(object obj)
{
int? genericObj = 23;
var bar1 = IsNullable(genericObj); // Works
var bar2 = IsNullable(obj); // Doesn't work (type parameter is Object)
}
In conclusion: the general problem is not to determine whether a type is nullable, but to get at the type in the first place.
So, my challenge is: how to determine if a runtime object (the obj parameter in the above example) is nullable? Blow me away :)
Well, you're too late. A boxed value no longer has the type information you seek - boxing a int? value results either in null, or a boxed int. In a way, it's an analogue to calling GetType on null - it doesn't really make sense, there's no type information.
If you can, stick with generic methods instead of boxing as much as possible (dynamic can be very helpful for some of the interfaces between actual nullable values and objects). If you can't, you'll have to use your own "boxing" - or even just creating your own Nullable-like type, that will be a class rather than a very hacky struct-like thing :D
If needed, in fact, you can even make your Nullable type a struct. It's not the struct-ness that breaks the type information - it's not the boxing itself that destroys that information, it's the CLR hacks that enable Nullable to match the performance of non-nullable values. It's very smart and very useful, but it breaks some reflection-based hacks (like the one you're trying to do).
This works as expected:
struct MyNullable<T>
{
private bool hasValue;
private T value;
public static MyNullable<T> FromValue(T value)
{
return new MyNullable<T>() { hasValue = true, value = value };
}
public static implicit operator T (MyNullable<T> n)
{
return n.value;
}
}
private bool IsMyNullable(object obj)
{
if (obj == null) return true; // Duh
var type = obj.GetType().Dump();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(MyNullable<>);
}
Doing the same with System.Nullable doesn't; even just doing new int?(42).GetType() gives you System.Int32 instead of System.Nullable<System.Int32>.
System.Nullable not a real type - it gets special treatment by the runtime. It does stuff that you simply can't replicate with your own types, because the hack isn't even in the type definition in IL - it's right in the CLR itself. Another nice abstraction leak is that a nullable type is not considered a struct - if your generic type constraint is struct, you can't use a nullable type. Why? Well, adding this constraint means it's legal to use e.g. new T?() - which wouldn't be possible otherwise, since you can't make a nullable of a nullable. It's easy with the MyNullable type I've written here, but not with the System.Nullable.
EDIT:
The relevant part of the CLI specification (1.8.2.4 - Boxing and unboxing a value):
All value types have an operation called box. Boxing a value of any
value type produces its boxed value; i.e., a value of the
corresponding boxed type containing a bitwise copy of the original
value. If the value type is a nullable type—defined as an
instantiation of the value type System.Nullable—the result is a
null reference or bitwise copy of its Value property of type T,
depending on its HasValue property (false and true, respectively). All
boxed types have an operation called unbox, which results in a managed
pointer to the bit representation of the value.
So by definition, the box operation on nullable types produces either a null reference or the stored value, never a "boxed nullable".
If have the following method:
static void DoSomethingWithTwoNullables(Nullable<int> a, Nullable<int> b)
{
Console.WriteLine("Param a is Nullable<int>: " + (a is Nullable<int>));
Console.WriteLine("Param a is int : " + (a is int));
Console.WriteLine("Param b is Nullable<int>: " + (b is Nullable<int>));
Console.WriteLine("Param b is int : " + (b is int));
}
When i call this method with null as a parameter, the type check returns false for this parameter. For example this code
DoSomethingWithTwoNullables(5, new Nullable<int>());
results in this output:
Param a is Nullable<int>: True
Param a is int : True
Param b is Nullable<int>: False
Param b is int : False
Is there any way to preserve the type information when using a Nullable and passing null? I know that checking the type in this example is useless, but it illustrates the problem. In my project, I pass the parameters to another method that takes a params object[] array and tries to identify the type of the objects. This method needs to do different things for Nullable<int> and Nullable<long>.
Going straight to the underlying problem, no, you can't do this. A null Nullable<int> has exactly the same boxed representation as a null Nullable<long>, or indeed a 'normal' null. There is no way to tell what 'type' of null it is, since its underlying representation is simply all-zeros. See Boxing Nullable Types for more details.
conceptually, new Nullable<int> is null.
If we generalise, forgetting about Nullable<T>:
string s = null;
bool b = s is string;
we get false. false is the expected value for a type-check on a null value.
You can try using Reflection to achieve this. Relevant article here.
Unfortunately, null does not point to any specific memory location, and thus there is no metadata that you can associate with it to lookup the type. Thus, you cannot gain any additional information about the variable.
Unless I'm misunderstanding the question, you can get the type using GetType(). For example,
int? myNullableInt = null;
Console.WriteLine(myNullableInt.GetValueOrDefault().GetType());
If myNullableInt is null, a default value will be returned. Check the type of this returned value and, in this case, it will return System.Int32. You can do an If..else/Switch check on the returned type to perform the relevant action.
(int? is the same as Nullable<int>)
You can't do this, nor should you want to. Since Nullable<T> is a struct, value-type variables of this type have all the type information you need at compile time. Just use the typeof operator.
On the other hand, you might have a Nullable instance whose type you don't know at compile time. That would have to be a variable whose static type is object or some other reference type. Howver, because a Nullable<T> value boxes to a boxed T value, there's no such thing as a boxed Nullable<T>. That instance whose type your checking will just be a T.
This is why you get the same result for is int and is Nullable<int>. There's no way to distinguish between a boxed int and a boxed int?, because there is no boxed int?.
See Nulls not missing anymore for details.
As it has already been pointed out null has no type. To figure out if something is int? vs long? you need to use reflection to get information about something storing the type. Here is some code that you may be able to use as inspiration (not knowing exactly what you try to achieve the code is a bit weird):
class Pair<T> where T : struct {
public Pair(T? a, T? b) {
A = a;
B = b;
}
public T? A { get; private set; }
public T? B { get; private set; }
}
void DoSomething<T>(Pair<T> pair) where T : struct {
DoMore(pair);
}
void DoMore(params object[] args) {
Console.WriteLine("Do more");
var nullableIntPairs = args.Where(IsNullableIntPair);
foreach (Pair<int> pair in nullableIntPairs) {
Console.WriteLine(pair.A);
Console.WriteLine(pair.B);
}
}
bool IsNullableIntPair(object arg) {
var type = arg.GetType();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(Pair<>)
&& type.GetGenericArguments()[0] == typeof(int);
}
If you execute the following code
DoSomething(new Pair<int>(5, new int?()));
DoSomething(new Pair<long>(new long?(), 6L));
you get the following output:
Do more
5
null
Do more
You can use typeof :
a == typeof(Nullable<int>) //true
a == typeof(int) //false
I have the following:
KeyValuePair<string, string>? myKVP;
// code that may conditionally do something with it
string keyString = myKVP.Key;
// throws 'System.Nullable<System.Collections.Generic.KeyValuePair<string,string>>'
// does not contain a definition for 'Key'
I'm sure there is some reason for this as I can see that the type is nullable. Is it because I am trying to access the key when null could cause bad things to happen?
Try this instead:
myKVP.Value.Key;
Here is a stripped down version of System.Nullable<T>:
public struct Nullable<T> where T: struct
{
public T Value { get; }
}
Since the Value property is of type T you must use the Value property to get at the wrapped type instance that you are working with.
Edit: I would suggest that you check the HasValue property of your nullable type prior to using the Value.
if (myKVP.HasValue)
{
// use myKVP.Value in here safely
}
This is because nullable types can be assigned null value or the actual value, hence you have to call ".value" on all nullable types. ".value" will return the underlying value or throw a System::InvalidOperationException.
You can also call ".HasValue" on nullable type to make sure that there is value assigned to the actual type.