.NET - получить значение по умолчанию для отраженного PropertyInfo - PullRequest
55 голосов
/ 02 января 2009

Это действительно ставит меня в тупик сегодня. Я уверен, что это не так сложно, но у меня есть объект System.Reflection.PropertyInfo. Я хочу установить его значение на основе результата поиска в базе данных (например, ORM, сопоставление столбца со свойством).

Моя проблема в том, что если возвращаемым значением БД является DBNull, я просто хочу установить для свойства значение по умолчанию, так же, как при вызове:

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

Однако метод 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);

Выше не компилируется. по умолчанию (тип) является недействительным. Я тоже думал о выполнении:

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

Однако, если Type является строкой, это присвоит значение «new String ()», но я действительно хочу «null», что и возвращает «default (string)».

Так чего мне здесь не хватает? Я полагаю, что действительно хакерский способ - создать новый экземпляр типа myObj и скопировать его свойство, но это просто кажется глупым ...

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

Я бы предпочел не тратить память на создание совершенно нового экземпляра, просто чтобы получить значение по умолчанию для этого свойства. Кажется очень расточительным.

Есть идеи?

Ответы [ 6 ]

50 голосов
/ 02 января 2009

Я верю, если вы просто сделаете

prop.SetValue(obj,null,null);

Если это тип значения, для него будет установлено значение по умолчанию, а для ссылочного типа будет установлено значение null.

47 голосов
/ 02 января 2009
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
29 голосов
/ 03 января 2009

Трюк "null" установит для этого типа значение ноль , которое не обязательно совпадает со значением по умолчанию для свойства. Во-первых, если это новый объект, почему бы просто не оставить его в покое? Или используйте TypeDescriptor:

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

При этом учитываются шаблоны [DefaultValue] и Reset{name}() (используемые при связывании и сериализации), что делает его очень универсальным и пригодным для повторного использования.

Если вы делаете это много, вы также можете получить повышение производительности, используя TypeDescriptor вместо отражения, повторно используя PropertyDescriptorCollection и используя HyperDescriptor (тот же код, но намного быстрее, чем отражение или необработанное TypeDescriptor).

13 голосов
/ 24 октября 2011

Попробуйте следующие методы, которые я написал и протестировал на тысячах типов:

    /// <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 #, поскольку вы можете просто использовать ключевое слово по умолчанию (T).

Наслаждайтесь!

0 голосов
/ 03 апреля 2018

Я знаю, что это старый пост, но мне нравится этот поворот в ответе Дарина Димитрова. Сначала он проверяет любые атрибуты DefualtValue, а затем использует ответ Дарина Димитрова :

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;
    }
0 голосов
/ 23 июля 2015

Это более отлаженная версия, поддерживающая функциональность .NET Runtime без добавления ненужного пользовательского кода.

ПРИМЕЧАНИЕ. Этот код написан для .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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...