.NET - 获取反映的PropertyInfo的默认值

时间:2021-03-07 16:24:24

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).

今天这真的让我很难过。我确定它不是那么难,但我有一个System.Reflection.PropertyInfo对象。我想根据数据库查找的结果设置其值(想想ORM,将列映射回属性)。

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:

我的问题是如果DB返回值是DBNull,我只想将属性值设置为其默认值,与调用相同:

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:

但是,如果你给它一个Type,那么default()方法将不会编译,这就是我所拥有的:

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.

但是,如果Type是字符串,那将赋值“new String()”,但我真的想要“null”,这就是“default(string)”将返回的内容。

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...

那我在这里错过了什么?我想一个非常hacky的方法是创建myObj的Type的新实例并复制属性,但这看起来很愚蠢......

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?

有任何想法吗?

6 个解决方案

#1


46  

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.

如果它是一个valuetype,它会将它设置为默认值,如果它是一个引用类型,它会将它设置为null。

#2


47  

object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;

#3


26  

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:

“null”技巧会将其设置为类型的零值,该值不一定与属性的默认值相同。首先,如果它是一个新对象,为什么不单独留下它?或者,使用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.

这同时尊重[DefaultValue]和Reset {name}()模式(由绑定和序列化使用),使其非常通用和可重用。

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).

如果你正在做很多这样的事情,你也可以通过重复使用PropertyDescriptorCollection和使用HyperDescriptor(相同的代码,但比refletion或原始的TypeDescriptor快得多),使用TypeDescriptor而不是反射来提升性能。

#4


12  

Try the following methods, which I have written and tested against thousands of types:

尝试以下方法,我已经编写并测试了数千种类型:

    /// <summary>
    /// [ <c>public static T GetDefault&lt; T &gt;()</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&lt;T&gt;"/>
    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.

GetDefault的第一个(通用)版本当然是C#的冗余版本,因为您可能只使用默认(T)关键字。

Enjoy!

请享用!

#5


0  

This is a more polished version that maintains the .NET Runtime's functionality without adding any unnecessary custom code.

这是一个更加精致的版本,可以维护.NET Runtime的功能,而无需添加任何不必要的自定义代码。

NOTE: This code written for .NET 3.5 SP1

注意:此代码是为.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)

拉沙德里维拉(OmegusPrime.com)

#6


0  

I know this is an old post, but I like this twist on Darin Dimitrov's answer. It first checks for any DefualtValue attributes then uses Darin Dimitrov's answer:

我知道这是一个老帖子,但我喜欢Darin Dimitrov的回答。它首先检查任何DefualtValue属性,然后使用Darin Dimitrov的答案:

public static object GetDefaultValueForProperty(this PropertyInfo property)
    {
        var defaultAttr = property.GetCustomAttribute(typeof(DefaultValueAttribute));
        if (defaultAttr != null)
            return (defaultAttr as DefaultValueAttribute).Value;

        var propertyType = property.PropertyType;
        return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
    }

#1


46  

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.

如果它是一个valuetype,它会将它设置为默认值,如果它是一个引用类型,它会将它设置为null。

#2


47  

object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;

#3


26  

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:

“null”技巧会将其设置为类型的零值,该值不一定与属性的默认值相同。首先,如果它是一个新对象,为什么不单独留下它?或者,使用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.

这同时尊重[DefaultValue]和Reset {name}()模式(由绑定和序列化使用),使其非常通用和可重用。

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).

如果你正在做很多这样的事情,你也可以通过重复使用PropertyDescriptorCollection和使用HyperDescriptor(相同的代码,但比refletion或原始的TypeDescriptor快得多),使用TypeDescriptor而不是反射来提升性能。

#4


12  

Try the following methods, which I have written and tested against thousands of types:

尝试以下方法,我已经编写并测试了数千种类型:

    /// <summary>
    /// [ <c>public static T GetDefault&lt; T &gt;()</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&lt;T&gt;"/>
    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.

GetDefault的第一个(通用)版本当然是C#的冗余版本,因为您可能只使用默认(T)关键字。

Enjoy!

请享用!

#5


0  

This is a more polished version that maintains the .NET Runtime's functionality without adding any unnecessary custom code.

这是一个更加精致的版本,可以维护.NET Runtime的功能,而无需添加任何不必要的自定义代码。

NOTE: This code written for .NET 3.5 SP1

注意:此代码是为.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)

拉沙德里维拉(OmegusPrime.com)

#6


0  

I know this is an old post, but I like this twist on Darin Dimitrov's answer. It first checks for any DefualtValue attributes then uses Darin Dimitrov's answer:

我知道这是一个老帖子,但我喜欢Darin Dimitrov的回答。它首先检查任何DefualtValue属性,然后使用Darin Dimitrov的答案:

public static object GetDefaultValueForProperty(this PropertyInfo property)
    {
        var defaultAttr = property.GetCustomAttribute(typeof(DefaultValueAttribute));
        if (defaultAttr != null)
            return (defaultAttr as DefaultValueAttribute).Value;

        var propertyType = property.PropertyType;
        return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
    }