代码之家  ›  专栏  ›  技术社区  ›  CodingWithSpike

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

  •  68
  • CodingWithSpike  · 技术社区  · 16 年前

    今天这真的让我很困惑。我肯定没那么难,但我有一个系统。反思。PropertyInfo对象。我想根据数据库查找的结果设置它的值(想想ORM,将列映射回属性)。

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

    value = default(T);  // where T is the type of the property.
    

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

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

    上述内容无法编译。默认值(类型)无效。我也考虑过做:

    object myObj = ???;
    PropertyInfo myPropInf = ???;
    myPropInf.SetValue(myObj, Activator.CreateInstance(myPropInf.PropertyType), null);
    

    但是,如果Type是字符串,则会分配值“new string()”,但我真的想要“null”,这就是“default(string)”将返回的值。

    那么,我在这里错过了什么? 我认为一种非常简单的方法是创建myObj的Type的新实例并复制该属性,但这似乎很愚蠢。..

    object myObj = ???;
    PropertyInfo  myPropInf = ???;
    var blank = Activator.CreateInstance(myObj.GetType());
    object defaultValue = myPropInf.GetValue(blank, null);
    myPropInf.SetValue(myObj, defaultValue, null);
    

    我宁愿不浪费内存来创建一个全新的实例,只是为了获得属性的默认值。看起来很浪费。

    有什么想法吗?

    6 回复  |  直到 16 年前
        1
  •  49
  •   BFree    16 年前

    我相信如果你这样做

    prop.SetValue(obj,null,null);
    

    如果是值类型,则将其设置为默认值;如果是引用类型,则设置为null。

        2
  •  47
  •   Darin Dimitrov    16 年前
    object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
    
        3
  •  28
  •   Marc Gravell    16 年前

    “null”技巧会将其设置为 类型的值,该值不一定与属性的默认值相同。首先,如果它是一个新对象,为什么不把它放在一边呢?或者,使用 TypeDescriptor :

    PropertyDescriptor prop = TypeDescriptor.GetProperties(foo)["Bar"];
    if (prop.CanResetValue(foo)) prop.ResetValue(foo);
    

    这既尊重 [DefaultValue] 以及 Reset{name}() 模式(如绑定和序列化所使用的),使其非常通用和可重用。

    如果你做了很多这样的事情,你也可以通过使用 类型描述符 通过重复使用 PropertyDescriptorCollection 并使用 HyperDescriptor (代码相同,但 比反射或原始更快 类型描述符 ).

        4
  •  13
  •   Mark Jones    13 年前

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

        /// <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");
        }
    

    GetDefault的第一个(通用)版本对于C#来说当然是多余的,因为您可以只使用default(T)关键字。

    享受!

        5
  •  0
  •   RashadRivera    10 年前

    我知道这是一篇老文章,但我喜欢Darin Dimitrov的回答。它首先检查是否有任何DefaultValue属性,然后使用 Darin Dimitrov's answer :

    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;
            propertyType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
            return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
        }
    
        6
  •  0
  •   rahicks    7 年前

    这是一个更精致的版本,可以维护。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);
        }
    }
    

    }

    用法:

    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();
    

    拉沙德·里维拉(OmegusPrime.com)

    推荐文章