简体   繁体   中英

C# Determine difference between DateTime and DateTime? (Nullable)

I am trying to determine the difference between a DateTime and a DateTime? using reflection. Please see my test code below:

    public class TestClass
    {
        public DateTime testDate1 { get; set; }
        public DateTime? testDate2 { get; set; }
    }

    public void Test()
    {
        TestClass testing = new TestClass();
        var props = typeof(TestClass).GetProperties();
        foreach (PropertyInfo p in props)
        {
            object o = p.GetValue(testing);
            if (typeof(DateTime?).IsInstanceOfType(o))
            {
                o = DateTime.Now;
            }
            if (typeof(DateTime).IsInstanceOfType(o))
            {
                if (((DateTime)o) == DateTime.MinValue)
                {
                    o = null;
                }
            }
            Console.WriteLine(string.Format("{0} = {1}", p.Name, (o ?? "NULL").ToString()));
        }

    }

The output of this code is the opposite to what i would expect. Currently the output is:

testDate1 = 26/01/2016 16:15:00

testDate2 = NULL

I am expecting testDate1 to be null and testDate2 to contain the value.

When debugging this code, it seems that the first pass using testDate1 passes both of the typeof if statements, the second fails both of the if statements. Can anybody help me understand and hopefully try and catch the specific nullable instance of the date time?

To note i have also tried switching to a definition and test on Nullable just in case, but it made no difference.

Many thanks!

The answer is you can't.

If you have an object date there is no way to know if it orginally comes from a nullabe DateTime or not.

DateTime? nullable = DateTime.Now;
object o = nullable;

The assignment to o is morally equivalent to

object temp = null;

if (nullable.HasValue)
{
    temp = nullable.Value;
}

o = temp;

As you can see, the information of the nullable type is lost and what you are really getting is the boxed nullable's value.

This is easy to see by simply doing o.GetType() which will return DateTime if the nullable had a value or you will get a NullReferenceException if it didn't.

This does not mean that you can not determine if the declared type of a property, method or member is nullable or not. You can do this easily enough via reflection.

First, keep in mind that testDate1 can never be null because DateTime is a struct . If you declare a DateTime without intitializing it, it will get the default value of DateTime.MinValue . testDate2 , on the other hand, is a Nullable struct, where the default value is null(-ish... it's actually a value that represents null, but not null itself).

IsInstanceOfType is using o 's GetType() method in the background to verify that its type is the same as the type you are comparing to (in this case, DateTime? ). However, if you take a look at the documentation , it states:

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.

So, if you step through the foreach loop for the testDate1 property, you'll see that the first condition will return true (since testDate1 can't be null, which means it has to be of type DateTime ). You then step into the second if-condition (though that's effectively just doing the same check again), but you don't enter the inner if because o currently has a value of DateTime.Now .

Now stepping through for testDate2 (which is holds a null value), you will see that you don't enter either of the if-conditionals because null doesn't have a type. As such, your object will remain null throughout the second iteration of the loop, giving you the output you see there.


NOTE: Just so you know, the way you're "assigning" values to o doesn't modify the original TestClass at all. Consider the following:

TestClass testing = new TestClass();
var prop = typeof(TestClass).GetProperty("testDate1");

// Here we get the current value of testing.testDate1, which is the default DateTime.MinValue
object o = prop.GetValue(testing);

// Here we set o to null... has ZERO effect on testing.testDate1. 
o = null;

// What you actually want is probably the following.
// (Remember though that testDate1 can't be null... this just sets it back to DateTime.MinValue.)
prop.SetValue(testing, null);

Thank you for so much useful comments and links. I realise that it was the property i should have been inspecting and not the object. I am using this:

Nullable.GetUnderlyingType(p.PropertyType)

It allows me to determine what i am dealing with and is pretty effective. I still end up switching on typeof(T).Name but i will cross that bridge when i come to it :)

I have to live with the fact that functional code vs ugly code remains an internal battle!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM