简体   繁体   中英

C# how to determine value wrapped within object is null or default?

I am currently writing on a custom required validation attribute, and I want to revert with the error when the value is null or Default. With Default I mean "" for String, 0 for int, 0.0 for double, null for object.

And to achieve that, I am calling the below function that works well for any type

    protected bool IsNullOrDefault<T>(T value)
    {
        return object.Equals(value, default(T));
    }

Here are the tests:

    object obj = null;
    bool flag = IsNullOrDefault(obj));

flag = True

    int i = 0;
    bool flag = IsNullOrDefault(i);

flag = True

    double d = 0.0;
    Console.WriteLine(IsNullOrDefault(d));

flag = True

    object value = 0;
    Console.WriteLine(IsNullOrDefault(value));

flag = False

Here the object in-turn contains int inside, but it still thinks it is an object, whose default value is null and current value is 0 . So it returns False .

The problem is, the framework method I am overriding gives me the value as object, so it will always going to match with the last scenario mentioned above.

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    { ....
    }

As per the value how can we convert the object to the actual type which in this case is int ? So that, I receive True even for the last scenario above.

You have to realize that the values returned by default and typeof operators are determined upon compile-time , and once your T is inferred as object , you would always get your value compared to null .

Your best bet is to check the run-time type instead:

protected bool IsNullOrDefault<T>(T value)
{
    if (value == null)
        return true;

    var actualType = value.GetType();

    if (actualType.IsValueType)
        return value.Equals(Activator.CreateInstance(actualType));

    return false;
}
 object value = 0; Console.WriteLine(IsNullOrDefault(value)); 

In this case T is System.Object and thus default(T) is null .

You need to check the runtime type not the compile time type to handle this:

public bool IsNullOrDefault(object value) {
  if (value is Int32) {
    return (Int32)value == 0;
  } else if (value is Int64) {
    return (Int64)value == 0L;
  } else if …
  } else {
    return value == null;
  }
}

covering all the types you might encounter.

Finally I am able to achieve this by casting the value with dynamic type,

    protected bool IsNullOrDefault(object obj)
    {
        bool retval = false;
        if (obj == null)
        {
            retval = true;
        }
        else
        {
            retval = IsEmpty((dynamic)obj);
        }
        return retval;
    }

    protected bool IsEmpty<T>(T value)
    {
        return object.Equals(value, default(T));
    }

You should use dynamic type where you want the resolution of the type to take place at run time rather than at compile time. Because of this, in this case the value self determines it's type appropriately without we telling it.

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