Type checking on Nullable<int> - c#

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

Related

Why is "default" different from "null" when assigning to a nullable type constrained by notnull?

Consider this minimal-ish example:
class A<T> where T : notnull
{
public T? Item; // OK!
public T? Something() => default; // OK!
public void Meow(ref T? thing1, out T? thing2) { thing2 = default; } // OK!
void Something(A<T>? other)
{
// OK!
Item = other is null ? default : other.Item;
// Error CS0403 : Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
Item = other is null ? null : other.Item;
// Error CS8978 : 'T' cannot be made nullable.
Item = other?.Item;
Item = other?.Item ?? default;
var x = other?.Item;
}
void Weird<U>(A<U>? other) where U : unmanaged
{
// OK!
var x = other?.Item;
}
void Weird2<U>(A<U>? other) where U : class
{
// OK!
var x = other?.Item;
}
void Weird3<U>(A<U>? other) where U : struct
{
// OK!
var x = other?.Item;
}
}
T is notnull
T? is either T or null ... isn't it? In what case would that NOT be true?
If T is struct then Item is Nullable<T>, which can be assigned by null
If T is class then 'Item' is simply T? which also can be assigned by null
I even made T unmanaged just to double check (it should be a subset of struct), and that can be assigned by null
...
So what type T exists such that T? could not hold the value null?
... I'm also not quite sure I'm even asking the right question ... My real question is "Why doesn't it work!!?!?!" :)
Note: A similar question was properly answered here: C#'s can't make `notnull` type nullable ... but that answer is applicable for C# 8 ... as of C# 9 and above, it's no longer the case
I'll also point out docs here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters ... which basically say that notnull means class/struct
...
Edit (afterthought): If it's an IL issue (that the IL needs to know whether it's a a nullable reference type or Nullable) ... Then why does default work and null doesn't? Clearly SOME version of IL works, so it's just a language deficiency/quirk?
Welp, I'm posting an answer (since nobody else did), after posting this as a bug here: https://github.com/dotnet/roslyn/issues/59805
The answer is that T? is not int? when T is an int. T? is simply int.
Which is nuts :)
It's even weirder because if you tell the compiler that T is a struct, then suddenly T? is int? when T is an int.
Weird. But okay. So be it.

GetType of nullable int returns same as GetType of int

I have a function that gets an expression as parameter. The type of the expression is Func<int?>. But when the expression is evaluated, the nullable int is unpacked.
See following Code:
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
var asd = new Asd() { Id = 1 };
Do(() => asd.Id);
Console.Read();
}
private static void Do<T>(Expression<Func<T>> expr)
{
var tType = typeof(T);
var type = expr.Compile()().GetType();
Console.Write($"T = {tType.Name}, obj type = {type.Name}");
}
class Asd
{
public int? Id { get; set; }
}
}
The console shows following output:
T = Nullable`1, obj type = Int32
Why does this happen and how can I avoid this?
Unfortunately I can not provide a dotnetfiddle because this code does not run in dotnetfiddle. maybe because of the expressions.
//Edit:
To make it clear: I need the property Id to be of type int? and I need to compile and execute the function. The problem here occurs in a bigger pice of code. This code is only the minimum required set of code to have a runnable example.
Best regards
//Edit 2:
It turned out that my small example produces errors, too, which let me think that it shows my actual problem. But of course it does not.
I used GetType() to show you that the functions return value is not of type Nullable<int> but of int. According to the links provided in the comments, GetType() induces a unboxing of the nullable and thus returns the underlying type.
My real problem was another one that maybe has similar reasons.
I used var valueExpr = Expression.Constant(valueFromImport); to create a constant expression with the nullable value int? valueFromImport = .... Later on I setup an expression to assign the constant to a nullable property. That failes with an exception:
System.ArgumentException: "Expression of type 'System.Int32' cannot be used for assignment to type 'System.Nullable`1[System.Int32]'"
The problem was, that Expression.Constant also seems to unbox the nullable value. When I use var valueExpr = Expression.Constant(valueFromImport, typeof(T)); everything works as expected.
What do you mean by "int is unpacked"? The result of the expressio still is of type int?, but you can't get it via reflection, which is the expected behavior. Citing msdn for Nullables:
Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.
Either way, what is your main problem? The result is of the correct type and you can still get it with typeof(T). The result of GetType shouldn't get in your way.

String is Nullable returns false

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

How to determine if a runtime object is of a nullable value type

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".

Generic typed function: return a non-nullable value

I wrote this trivial utility function:
public static T isNull<T>(T? v, T d)
{
return v == null ? d : v.Value;
}
the purpose is to avoid annoying task like check for a member to be null, very common in reading a linq recordset. The problem is that it's throwing this error:
The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable< T>'
The error seems legit, anyway I wish I can do something like this:
int? myField = record.myField;
int myValue = isNull(myField, 0);
instead of:
int? myField = record.myField;
int myValue = myField == null ? 0 : myField.Value;
I feel like I'm missing some c# basic knowledge. Does exist a way to accomplish my task?
Generic constraints can be used on generic functions to limit the types that are permitted to be used to certain subsets, and this opens up possibilities for how you might use those types inside your method or class.
In this case, you can apply a constraint to T that limits it to a struct to resolve your specific compiler error.
public static T IsNull<T>(T? v, T d) where T : struct
{
return v == null ? d : v.Value;
}
However, another answer does correctly point out that you could opt to use the null coalescing operator ?? in this particular situation.
This is called null coalescing, and there is a built in operator to do it:
int myValue = record.myField ?? 0
While the other answers are good, presumably you'd like to write your method so it would work with both reference and value types. You can accomplish that by having two overloads, both with generic constraints.
public static T IsNull<T>(T v, T d) where T : class
{
return v ?? d;
}
public static T IsNull<T>(T? v, T d) where T : struct
{
return v.HasValue ? v.Value : d;
}
NOTE: Calling IsNull with a value type other than Nullable<T> will still not compile. e.g.
string myString = ...
Console.WriteLine(IsNull(myString, "foo")) // works
object myObject = ...
Console.WriteLine(IsNull(myMyObject, new object())) // works
DateTime? myDateTime = ...
Console.WriteLine(IsNull(myDateTme, DateTme.Now)) // works
int? myInt1 = ...
Console.WriteLine(IsNull(myInt1, DateTme.Now)) // works
int myInt2 = ...
Console.WriteLine(IsNull(myInt2, DateTme.Now)) // <-- compiler error

Categories

Resources