Check to see if a given object (reference or value type) is equal to its default

17,922

Solution 1

In your example, your integer is boxed and therefore your T is going to be object, and the default of object is null, so that's not valuable to you. If the object is a value type, you could get an instance of it (which would be the default) to use as a comparison. Something like:

if (argument is ValueType)
{
   object obj = Activator.CreateInstance(argument.GetType());
   return obj.Equals(argument);
}

You'd want to deal with other possibilities before resorting to this. Marc Gravell's answer brings up some good points to consider, but for a full version of your method, you might have

public static bool IsNullOrDefault<T>(T argument)
{
    // deal with normal scenarios
    if (argument == null) return true;
    if (object.Equals(argument, default(T))) return true;

    // deal with non-null nullables
    Type methodType = typeof(T);
    if (Nullable.GetUnderlyingType(methodType) != null) return false;

    // deal with boxed value types
    Type argumentType = argument.GetType();
    if (argumentType.IsValueType && argumentType != methodType) 
    {
        object obj = Activator.CreateInstance(argument.GetType());
        return obj.Equals(argument);
    }

    return false;
}

Solution 2

if o is null, in a non-generic (object) method, you will have no access to the original type - and you can't do much about that.

Hence, the only time it matters is non-nullable value-types, so:

Type type = value.GetType();
if(!type.IsValueType) return false; // can't be, as would be null
if(Nullable.GetUnderlyingType(type) != null) return false; // ditto, Nullable<T>
object defaultValue = Activator.CreateInstance(type); // must exist for structs
return value.Equals(defaultValue);

Solution 3

Converted Anthony Pegram's Answer into an extension method:

using System;

//Adapted from https://stackoverflow.com/a/6553276/1889720
public static class ObjectExtensions
{
    public static bool IsNullOrDefault<TObject>(this TObject argument)
    {
        // deal with normal scenarios
        if (argument == null)
        {
            return true;
        }
        if (object.Equals(argument, default(TObject)))
        {
            return true;
        }

        // deal with non-null nullables
        Type methodType = typeof(TObject);
        if (Nullable.GetUnderlyingType(methodType) != null)
        {
            return false;
        }

        // deal with boxed value types
        Type argumentType = argument.GetType();
        if (argumentType.IsValueType && argumentType != methodType)
        {
            object obj = Activator.CreateInstance(argument.GetType());
            return obj.Equals(argument);
        }

        return false;
    }
}

Usage syntax:

myVariable.IsNullOrDefault();

Solution 4

Expanding on Marc Gravell's answer, by getting the run-time type as an argument:

// Handles boxed value types
public static bool IsNullOrDefault([CanBeNull] this object @object,
    [NotNull] Type runtimeType)
{
    if (@object == null) return true;

    if (runtimeType == null) throw new ArgumentNullException("runtimeType");

    // Handle non-null reference types.
    if (!runtimeType.IsValueType) return false;

    // Nullable, but not null
    if (Nullable.GetUnderlyingType(runtimeType) != null) return false;

    // Use CreateInstance as the most reliable way to get default value for a value type
    object defaultValue = Activator.CreateInstance(runtimeType);

    return defaultValue.Equals(@object);
}

For those of you that will challenge my use case, I want to list the values of properties on an arbitrary object, omitting properties which set to their defaults (for a more concise display).

Because propertyInfo.GetValue(targetObject, null) returns an object, and value types are boxed, I cannot use a generic method. By passing propertyInfo.PropertyType as the second parameter to this method I can avoid the problem a generic method has with boxed value types.

Solution 5

The following will sort it out.

    public static bool IsNullOrDefault<T>(T argument)
{
    if (argument is ValueType || argument != null)
    {
        return object.Equals(argument, GetDefault(argument.GetType()));
    }
    return true;
}


public static object GetDefault(Type type)
{
    if(type.IsValueType)
    {
        return Activator.CreateInstance(type);
    }
    return null;
}
Share:
17,922
Brian
Author by

Brian

Updated on June 13, 2022

Comments

  • Brian
    Brian about 2 years

    I'm trying to find a way to check and see if the value of a given object is equal to its default value. I've looked around and come up with this:

        public static bool IsNullOrDefault<T>(T argument)
        {
            if (argument is ValueType || argument != null)
            {
                return object.Equals(argument, default(T));
            }
            return true;
        }
    

    The problem I'm having is that I want to call it like this:

                object o = 0;
                bool b = Utility.Utility.IsNullOrDefault(o);
    

    Yes o is an object, but I want to make it figure out the base type and check the default value of that. The base type, in this case, is an integer and I want to know in this case if the value is equal to default(int), not default(object).

    I'm starting to think this might not be possible.