150 votes

Valeur par défaut d’un type à l’exécution

Pour tout type donné, je veux savoir sa valeur par défaut.

En C#, il y a un mot-clé par défaut pour ce faire, comme

object obj = default(Decimal);

mais j'ai une instance de Type (appelé myType) et si je dis cela,

object obj = default(myType);

il ne fonctionne pas

Est-il un bon moyen de faire cela? Je sais qu'un énorme bloc de commutateurs va fonctionner, mais ce n'est pas un bon choix.

252voto

Dean Harding Points 40164

Il n'y a vraiment que deux possibilités: null pour les types référence et new myType() pour les types de valeur (ce qui correspond à 0 pour les int, float, etc) Si vous avez vraiment seulement besoin de tenir compte de deux cas:

object GetDefaultValue(Type t)
{
    if (t.IsValueType)
        return Activator.CreateInstance(t);

    return null;
}

(Parce que les types de valeurs de toujours avoir un constructeur par défaut, qui appellent à l'Activateur.CreateInstance ne manquera jamais).

33voto

coalvilledave Points 860

Vous pouvez également l'ajouter comme une méthode d'extension du Système.Type:

public static class TypeExtensions
{
    public static object GetDefaultValue(this Type t)
    {
        if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
            return Activator.CreateInstance(t);
        else
            return null;
    }
}

18voto

Mark Jones Points 738

Après avoir résolu ce problème dans mon propre système, voici une méthode pour déterminer correctement la valeur par défaut de l'arbitraire d'un Type à l'exécution, qui a été testé contre des milliers de Types:

    /// <summary>
    /// [ <c>public static object GetDefault(this 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>
    /// <example>
    /// To use this method in its native, non-extension form, make a call like:
    /// <code>
    ///     object Default = DefaultValue.GetDefault(someType);
    /// </code>
    /// To use this method in its Type-extension form, make a call like:
    /// <code>
    ///     object Default = someType.GetDefault();
    /// </code>
    /// </example>
    /// <seealso cref="GetDefault&lt;T&gt;"/>
    public static object GetDefault(this 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/enum), 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");
    }

Dans ces exemples, la GetDefault méthode est implémentée dans la classe statique DefaultValue. Appeler cette méthode avec une instruction comme:

        object Default = DefaultValue.GetDefault(someType);

Pour utiliser le GetDefault méthode comme une méthode d'extension pour le Type, l'appeler comme ceci:

        object Default = someType.GetDefault();

Ce second Type d'extension de l'approche est plus simple client-syntaxe du code, car il élimine le besoin de faire référence à la Valeur par défaut de la classe qualificatif sur l'appel.

Le ci-dessus courent à temps la forme de GetDefault fonctionne à l'identique de la sémantique primitive de C# par défaut " mot-clé, et produit les mêmes résultats.

Pour utiliser une forme générique de GetDefault, vous pouvez accéder à la fonction suivante:

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

Un appel à la forme générique pourrait être quelque chose comme:

        int? inDefaultVal = DefaultValue.GetDefault<int?>();

Bien sûr, la forme générique de GetDefault est inutile pour le C#, car il fonctionne de la même comme valeur par défaut(T). Il est seulement utile pour une .NET de la langue qui ne prend pas en charge le "défaut" mot clé, mais qui prend en charge les types génériques. Dans la plupart des cas, la forme générique est inutile.

Un corollaire utile de la méthode est de déterminer si un objet contient la valeur par défaut pour son Type. J'ai aussi compter IsObjectSetToDefault méthode:

    /// <summary>
    /// [ <c>public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)</c> ]
    /// <para></para>
    /// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type
    /// </summary>
    /// <remarks>
    /// Reports whether the object is empty or unitialized for a reference type or nullable value type (i.e. is null) or 
    /// whether the object contains a default value for a non-nullable value type (i.e. int = 0, bool = false, etc.)
    /// <para></para>
    /// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty.
    /// </remarks>
    /// <param name="ObjectType">Type of the object to test</param>
    /// <param name="ObjectValue">The object value to test, or null for a reference Type or nullable value Type</param>
    /// <returns>
    /// true = The object contains the default value for its Type.
    /// <para></para>
    /// false = The object has been changed from its default value.
    /// </returns>
    public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)
    {
        // If no ObjectType was supplied, attempt to determine from ObjectValue
        if (ObjectType == null)
        {
            // If no ObjectValue was supplied, abort
            if (ObjectValue == null)
            {
                MethodBase currmethod = MethodInfo.GetCurrentMethod();
                string ExceptionMsgPrefix = currmethod.DeclaringType + " {" + currmethod + "} Error:\n\n";
                throw new ArgumentNullException(ExceptionMsgPrefix + "Cannot determine the ObjectType from a null Value");
            }

            // Determine ObjectType from ObjectValue
            ObjectType = ObjectValue.GetType();
        }

        // Get the default value of type ObjectType
        object Default = ObjectType.GetDefault();

        // If a non-null ObjectValue was supplied, compare Value with its default value and return the result
        if (ObjectValue != null)
            return ObjectValue.Equals(Default);

        // Since a null ObjectValue was supplied, report whether its default value is null
        return Default == null;
    }

Le ci-dessus IsObjectSetToDefault méthode peut être appelée dans sa forme native ou accessible comme un Type de classe de l'extension.

Profitez-en!

Marque

2voto

Michael Stum Points 72046

Qu'entendez-vous par "Valeur par Défaut"? Tous les Types de référence ("classe"), null comme valeur par défaut, alors que tous les types de valeur ont leurs valeurs par défaut en fonction de ce tableau.

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