65 votes

.NET - Obtenir la valeur par défaut pour un reflété PropertyInfo

C'est vraiment estompe moi aujourd'hui. Je suis sûr que ce n'est pas si dur, mais j'ai un Système.De la réflexion.PropertyInfo objet. Je veux définissez sa valeur sur la base du résultat d'une recherche de base de données (pensez à l'ORM, le mappage d'une colonne à une propriété).

Mon problème est que si la DB, la valeur de retour est DBNull, je veux juste pour définir la valeur de la propriété à sa valeur par défaut, le même que celui d'un appel:

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

Toutefois, la valeur par défaut() la méthode ne compile pas si vous lui donnez un Type, qui est ce que j'ai:

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

Le ci-dessus ne compile pas. par défaut(Type) n'est pas valide. J'ai aussi pensé faire:

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

Toutefois, si le Type est une chaîne, qui permettrait d'affecter la valeur "new String()", mais je veux vraiment "nul", ce qui est "par défaut(string)" serait de retour.

Donc ce qui me manque ici? Je suppose que vraiment hacky moyen serait de créer une nouvelle instance de myObj du Type et de la copie de la propriété, mais il semble tout simplement stupide...

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

Je préfère ne pas perdre la mémoire pour faire une toute nouvelle instance, juste pour obtenir la valeur par défaut pour la propriété si. Semble très inutile.

Des idées?

61voto

BFree Points 46421

Je crois que si vous venez de le faire

prop.SetValue(obj,null,null);

Si c'est un type valeur, c'vais le mettre à la valeur par défaut, si c'est un type de référence, c'vais le mettre à null.

55voto

Darin Dimitrov Points 528142
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;

34voto

Marc Gravell Points 482669

Le "null" truc " va le mettre à zéro la valeur pour le type, qui n'est pas nécessairement la même que la valeur par défaut pour la propriété. Tout d'abord, si c'est un nouvel objet, pourquoi ne pas la laisser tranquille? Sinon, vous pouvez utiliser TypeDescriptor:

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

Ce respect de l' [DefaultValue] et de la Reset{name}() modèles (comme utilisé par la liaison et la sérialisation), ce qui rend très polyvalent et ré-utilisable.

Si vous faites beaucoup de cela, vous pouvez également obtenir un gain de performances à l'aide de TypeDescriptor , au lieu de la réflexion, par la ré-utilisation de l' PropertyDescriptorCollection et à l'aide de HyperDescriptor (même code, mais beaucoup plus rapide que ce soit refletion ou raw TypeDescriptor).

13voto

Mark Jones Points 738

Essayez les méthodes suivantes, que j'ai écrit et testé sur des milliers de 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");
    }

La première (générique) version de GetDefault est bien sûr inutile pour C#, puisque vous pouvez simplement utiliser la valeur par défaut(T) mot-clé.

Profitez-en!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X