1

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.

Mukesh Bhojwani
  • 2,014
  • 4
  • 20
  • 35
  • 2
    "As per the value how can we convert the object to the actual type which in this case is int?" It isn't that though - it's just a null reference. It is the *exact* same value as if you'd written `object value = null;`. There's no way of getting from a null reference to "the type of value it would have been if it weren't null". – Jon Skeet Jul 07 '16 at 10:40

3 Answers3

5

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;
}
haim770
  • 48,394
  • 7
  • 105
  • 133
0
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.

Richard
  • 106,783
  • 21
  • 203
  • 265
  • I don't want to put the conditions for various types, do you have any other way where the conversion takes place without we telling it? – Mukesh Bhojwani Jul 07 '16 at 10:47
  • Putting every possible value-type in will be a nightmare, in particular if you have some user-defined structs also. – MakePeaceGreatAgain Jul 07 '16 at 10:59
  • @HimBromBeere Yes it would. Using `Type.IsAssignableFrom`, `Type.IsValueType`, and so forth would allow some reduction in the number of cases, but it is still an open ended list. – Richard Jul 07 '16 at 11:58
  • @MukeshBhojwani See my previous comment, but ultimately `default(T)` is compile time, but you need runtime. – Richard Jul 07 '16 at 11:59
0

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.

Mukesh Bhojwani
  • 2,014
  • 4
  • 20
  • 35