. NET-Obtener el valor predeterminado de un PropertyInfo reflejado


Esto realmente me estremece hoy. Estoy seguro de que no es tan difícil, pero tengo un Sistema.Reflexión.Objeto PropertyInfo. Quiero establecer su valor basado en el resultado de una búsqueda de base de datos (piense think, mapeando una columna de nuevo a una propiedad).

Mi problema es que si el valor devuelto por DB es DBNull, solo quiero establecer el valor de la propiedad a su valor predeterminado, lo mismo que llamar:

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

Sin embargo, el método default () no se compilará si le das un Tipo, que es lo que tienen:

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

Lo anterior no compila. el valor predeterminado (Tipo) no es válido. También pensé en hacer:

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

Sin embargo, si el Tipo es string, eso asignaría el valor "new String()", pero realmente quiero "null", que es lo que "default(string)" devolvería.

Entonces, ¿qué me estoy perdiendo aquí? Supongo que una forma realmente hacky sería crear una nueva instancia del tipo de myObj y copiar la propiedad, pero eso parece estúpido...

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

Prefiero no desperdiciar la memoria para cree una instancia completamente nueva, solo para obtener el valor predeterminado de la propiedad. Parece un desperdicio.

¿Alguna idea?

Author: CodingWithSpike, 2009-01-02

6 answers

Creo que si solo lo haces

prop.SetValue(obj,null,null);

Si es un valuetype, lo establecerá en el valor predeterminado, si es un tipo de referencia, lo establecerá en null.

 46
Author: BFree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-02 16:43:12
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
 47
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-02 16:37:30

El truco "null" lo establecerá en el valor zero para el tipo, que no es necesariamente el mismo que el valor predeterminado para la propiedad. En primer lugar, si es un objeto nuevo, ¿por qué no dejarlo en paz? Alternativamente, use TypeDescriptor:

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

Esto respeta tanto [DefaultValue] como los patrones Reset{name}() (como se usan en la unión y la serialización), lo que lo hace muy versátil y reutilizable.

Si está haciendo mucho de esto, también puede obtener un aumento de rendimiento utilizando TypeDescriptor en lugar de reflexión, por reutilizar el PropertyDescriptorCollection y usar HyperDescriptor (mismo código, pero mucho más rápido que refletion o raw TypeDescriptor).

 27
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 10:37:54

Pruebe los siguientes métodos, que he escrito y probado contra miles de tipos:

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

La primera versión (genérica) de getDefault es, por supuesto, redundante para C#, ya que puede usar la palabra clave default(T).

Disfrute!

 13
Author: Mark Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-23 21:42:36

Esta es una versión más pulida que mantiene la funcionalidad del tiempo de ejecución de.NET sin agregar ningún código personalizado innecesario.

NOTA: Este código escrito para. 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);
    }
}

}

USO:

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)

 0
Author: RashadRivera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-23 15:28:16

Sé que este es un post antiguo, pero me gusta este giro en la respuesta de Darin Dimitrov. Primero verifica cualquier atributo DefualtValue y luego usa La respuesta de 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;
    }
 0
Author: rahicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-03 18:12:54