C# .NET - 获取反射的 PropertyInfo 的默认值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/407337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 02:11:22  来源:igfitidea点击:

.NET - Get default value for a reflected PropertyInfo

c#.netreflection

提问by CodingWithSpike

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:

但是,如果给它一个类型,则 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.

但是,如果类型是字符串,则会分配值“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类型的新实例并复制该属性,但这看起来很愚蠢......

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?

有任何想法吗?

采纳答案by BFree

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.

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

回答by Darin Dimitrov

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

回答by Marc Gravell

The "null" trick will set it to the zerovalue 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 TypeDescriptorinstead of reflection, by re-using the PropertyDescriptorCollectionand using HyperDescriptor(same code, but muchfaster than either refletion or raw TypeDescriptor).

如果你正在做大量的这个,你也可以使用的性能提升TypeDescriptor,而不是反射,通过-重新使用的PropertyDescriptorCollection,并使用HyperDescriptor(相同的代码,但很多比任何refletion或原始更快TypeDescriptor)。

回答by Mark Jones

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# 来说当然是多余的,因为您可以只使用 default(T) 关键字。

Enjoy!

享受!

回答by RashadRivera

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

这是一个更加完善的版本,它在不添加任何不必要的自定义代码的情况下保持了 .NET 运行时的功能。

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)

回答by rahicks

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