Reputation: 43748
This is really stumping me today. I'm sure its not that hard, but I have a System.Reflection.PropertyInfo object. I want to set its value based on the result of a database lookup (think ORM, mapping a column back to a property).
My problem is if the DB returned value is DBNull, I just want to set the property value to its default, the same as calling:
value = default(T); // where T is the type of the property.
However, the default() method won't compile if you give it a Type, which is what I have:
object myObj = ???; // doesn't really matter. some arbitrary class.
PropertyInfo myPropInf = ???; // the reflection data for a property on the myObj object.
myPropInf.SetValue(myObj, default(myPropInf.PropertyType), null);
The above doesn't compile. default(Type) is invalid. I also thought about doing:
object myObj = ???;
PropertyInfo myPropInf = ???;
myPropInf.SetValue(myObj, Activator.CreateInstance(myPropInf.PropertyType), null);
However, if the Type is string, that would assign the value "new String()", but I really want "null", which is what "default(string)" would return.
So what am I missing here? I suppose a really hacky way would be to create a new instance of myObj's Type and copy the property over, but that just seems stupid...
object myObj = ???;
PropertyInfo myPropInf = ???;
var blank = Activator.CreateInstance(myObj.GetType());
object defaultValue = myPropInf.GetValue(blank, null);
myPropInf.SetValue(myObj, defaultValue, null);
I'd rather not waste the memory to make a whole new instance, just to get the default for the property though. Seems very wasteful.
Any ideas?
Upvotes: 70
Views: 46953
Reputation: 603
I know this is an old post, but I like this twist on Darin Dimitrov's answer. It first checks for any DefaultValue attributes then uses Darin Dimitrov's answer:
public static object GetDefaultValueForProperty(this PropertyInfo property)
{
var defaultAttr = (DefaultValueAttribute)property.GetCustomAttribute(typeof(DefaultValueAttribute));
if (defaultAttr != null)
return defaultAttr.Value;
var propertyType = property.PropertyType;
propertyType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
}
Upvotes: 5
Reputation: 843
This is a more polished version that maintains the .NET Runtime's functionality without adding any unnecessary custom code.
NOTE: This code written for .NET 3.5 SP1
namespace System {
public static class TypedDefaultExtensions {
public static object ToDefault(this Type targetType) {
if (targetType == null)
throw new NullReferenceException();
var mi = typeof(TypedDefaultExtensions)
.GetMethod("_ToDefaultHelper", Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic);
var generic = mi.MakeGenericMethod(targetType);
var returnValue = generic.Invoke(null, new object[0]);
return returnValue;
}
static T _ToDefaultHelper<T>() {
return default(T);
}
}
}
USAGE:
PropertyInfo pi; // set to some property info
object defaultValue = pi.PropertyType.ToDefault();
public struct MyTypeValue { public int SomeIntProperty { get; set; }
var reflectedType = typeof(MyTypeValue);
object defaultValue2 = reflectedType.ToDefault();
Rashad Rivera (OmegusPrime.com)
Upvotes: 1
Reputation: 2064
Try the following methods, which I have written and tested against thousands of types:
/// <summary>
/// [ <c>public static T GetDefault< T >()</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <typeparam name="T">The Type for which to get the default value</typeparam>
/// <returns>The default value for Type T</returns>
/// <remarks>
/// If a reference Type or a System.Void Type is supplied, this method always returns null. If a value type
/// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an
/// exception.
/// </remarks>
/// <seealso cref="GetDefault(Type)"/>
public static T GetDefault<T>()
{
return (T) GetDefault(typeof(T));
}
/// <summary>
/// [ <c>public static object GetDefault(Type type)</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <param name="type">The Type for which to get the default value</param>
/// <returns>The default value for <paramref name="type"/></returns>
/// <remarks>
/// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value type
/// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an
/// exception.
/// </remarks>
/// <seealso cref="GetDefault<T>"/>
public static object GetDefault(Type type)
{
// If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
if (type == null || !type.IsValueType || type == typeof(void))
return null;
// If the supplied Type has generic parameters, its default value cannot be determined
if (type.ContainsGenericParameters)
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> contains generic parameters, so the default value cannot be retrieved");
// If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct), return a
// default instance of the value type
if (type.IsPrimitive || !type.IsNotPublic)
{
try
{
return Activator.CreateInstance(type);
}
catch (Exception e)
{
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " +
"create a default instance of the supplied value type <" + type +
"> (Inner Exception message: \"" + e.Message + "\")", e);
}
}
// Fail with exception
throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> is not a publicly-visible type, so the default value cannot be retrieved");
}
The first (generic) version of GetDefault is of course redundant for C#, since you may just use the default(T) keyword.
Enjoy!
Upvotes: 15
Reputation: 1064184
The "null" trick will set it to the zero value for the type, which is not necessarily the same as the default for the property. Firstly, if it is a new object, why not just leave it alone? Alternatively, use TypeDescriptor
:
PropertyDescriptor prop = TypeDescriptor.GetProperties(foo)["Bar"];
if (prop.CanResetValue(foo)) prop.ResetValue(foo);
This respects both [DefaultValue]
and the Reset{name}()
patterns (as used by binding and serialization), making it very versatile and re-usable.
If you are doing lots of this, you can also get a performance boost using TypeDescriptor
instead of reflection, by re-using the PropertyDescriptorCollection
and using HyperDescriptor (same code, but much faster than either refletion or raw TypeDescriptor
).
Upvotes: 38
Reputation: 103770
I believe if you just do
prop.SetValue(obj,null,null);
If it's a valuetype, it'll set it to the default value, if it's a reference type, it'll set it to null.
Upvotes: 63
Reputation: 1039498
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
Upvotes: 62