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.
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;
}
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);
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.
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;
}
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();
Make an extension method
public static class DateExtension
{
public static bool IsNullOrDefault(this DateTime? value)
{
return default(DateTime) == value || default(DateTime?) == value;
}
}
Solution with linq expressions. First call for type will be relatively slow, but then it should work just as quick as usual code.
public static class DefaultHelper
{
private delegate bool IsDefaultValueDelegate(object value);
private static readonly ConcurrentDictionary<Type, IsDefaultValueDelegate> Delegates
= new ConcurrentDictionary<Type, IsDefaultValueDelegate>();
public static bool IsDefaultValue(this object value)
{
var type = value.GetType();
var isDefaultDelegate = Delegates.GetOrAdd(type, CreateDelegate);
return isDefaultDelegate(value);
}
private static IsDefaultValueDelegate CreateDelegate(Type type)
{
var parameter = Expression.Parameter(typeof(object));
var expression = Expression.Equal(
Expression.Convert(parameter, type),
Expression.Default(type));
return Expression.Lambda<IsDefaultValueDelegate>(expression, parameter).Compile();
}
}
来源:https://stackoverflow.com/questions/6553183/check-to-see-if-a-given-object-reference-or-value-type-is-equal-to-its-default