Reflection - check all nullable properties have values - c#

I have to loop through all the properties in a few classes and check any nullable properties to see if they have a value. How do I cast the value returned from propertyInfo.GetValue() to a generic nullable type so that I can check the HasValue property?
Code snipped for brevity:
foreach (PropertyInfo propInfo in this.GetType().GetProperties())
{
if (<Snip: Check to see that this is a nullable type>)
{
//How do i cast this properly in here to allow me to do:
if(!((Nullable)propInfo.GetValue(this, null)).HasValue)
//More code here
}
}

note I'm assuming you mean Nullable<T>; if you mean Nullable<T> or a reference, then you already have it: object (from GetValue) - just check for null.
In the case of Nullable<T>; you can't cast to a single non-generic type (other than object) - but you don't need to; just check that it isn't null, since empty Nullable<T> is boxed to null, and GetValue returns object (hence it boxes the value).
if(Nullable.GetUnderlyingType(propInfo.PropertyType) != null) {
// it is a Nullable<T> for some T
if(propInfo.GetValue(this, null) != null) {
// it has a value (it isn't an empty Nullable<T>)
}
}
To clarify, Nullable is a static utility class that is completely separate to the Nullable<T> struct; so you don't cast to Nullable at all. As it happens, Nullable exists to provide things like the GetUnderlyingType that helps you work with Nullable<T>.

Since you've established that the property is of type Nullable<something>, you know its value has a HasValue property - so find that property by reflection and get its value.

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.

How do I tell the compiler about my nullable c# generic constraints?

I've got an editor that lets the user edit the "simple" properties of objects (int, string, DateTime, etc.), so it iterates through the properties and then constructs for each simple property an object to support the editing:
#nullable enable
public class DataNode<T>
{
protected PropertyInfo Prop;
protected object Source;
protected T Value;
protected bool Modified;
public DataNode(PropertyInfo prop, object source)
{
Prop = prop;
Source = source;
object? value = prop.GetValue(source);
if (value is T t)
Value = t;
else
Value = default;
Modified = false;
}
public virtual bool IsValid()
{
return true;
}
public void Save()
{
if (Modified)
{
Prop.SetValue(Source, Value);
Modified = false;
}
}
}
I then have subclasses for specific properties - for example, if there's an int property where I don't want the user to be able to enter a negative value, I can create a specific subclass:
public class NonNegativeIntNode : DataNode<int>
{
public NonNegativeIntNode (PropertyInfo prop, object source)
: base(prop, source)
{
}
public override bool IsValid()
{
return Value >= 0;
}
}
So that all works fine, and before you ask:
The selection of which DataNode class to use is controlled by custom attributes on the properties
The DataNode does more than this that I've left out for simplicity. For example the BoolDataNode class knows that to edit this value it should use a checkbox
The problem is that the compiler is grumbling that "Value = default;" is a possible null reference assignment, and that the DataNode constructor might be exiting with a null value for 'Value'.
I can keep the compiler happy by defining Value as "protected T? Value" but that adds unnecessary complication elsewhere to check for a null value when I know darn well that inside BoolDataNode that Value will never be null.
I tried splitting DataNode into two classes - NullableDataNode and NonNullableDataNode - but inside NonNullableDataNode, even though I specify "where T: notnull", the compiler is still worried about 'default'
It seems like saying "where T: notnull" means "I am never going to set Value to null" where what I want to tell the compiler is "T is a type that cannot be null" (like bool)
Is there a way to reassure the compiler that all is well, without simply turning off all the nullability warnings with pragmas?
I can keep the compiler happy by defining Value as "protected T? Value" but that adds unnecessary complication elsewhere to check for a null value when I know darn well that inside BoolDataNode that Value will never be null.
The right thing to do is to make this a protected T? Value field, since if T is a reference type, you'll be assigning null. People accessing your Value field need to know that if T is a non-nullable reference type, they might still be getting a null out.
You're confused about the meaning of T? when T is unconstrained however. T? means that the value is "defaultable", not "nullable". That's a subtle difference, but means that you can assign a value of default(T). Another way of putting that is:
If T is a reference type, T? means that you can assign null. If T is a value type, the ? in T? effectively has no meaning.
In other words, DataNode<string>.Value is of type string?, but DataNode<bool>.Value is of type bool, not bool?.
(Technically, there's no way to have T?, when T is unconstrained and is a value type, mean Nullable<T>. For nullable value types, the compiler outputs a member of type Nullable<T> rather than T. However, generics are expanded by the runtime rather than the compiler.)
Note that this changes if T is constrained to be a value type: in that case, T? suddenly starts meaning Nullable<T>.

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

The '.' operator on null nullable

How is it that you can access null nullable's propery HasValue?
I looked to the compiled code, and it's not a syntactic sugar.
Why this doesn't throw NullReferenceException:
int? x = null;
if (x.HasValue)
{...}
That's because int? is short for Nullable<int> which is a value type, not a reference type - so you will never get a NullReferenceException.
The Nullable<T> struct looks something like this:
public struct Nullable<T> where T : struct
{
private readonly T value;
private readonly bool hasValue;
//..
}
When you assign null there is some magic happening with support by the compiler (which knows about Nullable<T> and treats them special in this way) which just sets the hasValue field to false for this instance - which is then returned by the HasValue property.
Like BrokenGlass said, an int? is actually a Nullable<T>.
Structures always contain a value. Usually you cannot set a structure variable to null, but in this special case you can, essentially setting it to default(Nullable<T>). This sets its contents to null rather than the variable itself.
When you set a Nullable<T> to a value, it uses an implicit operator to set Value = value to the new value and HasValue = true.
When you set Nullable<T> to null, it nulls all of the structure's fields. For a bool field such as HasValue, null == false.
Since a Nullable<T> variable is a structure, the variable can always be referenced because its contents is null rather than the variable itself.
There's more information on structures in the Remarks section of the MSDN page struct.

Why does nullable KeyValuePair<,> have no key property?

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.

Categories

Resources