web-dev-qa-db-fra.com

Équivalent programmatique de défaut (Type)

J'utilise la réflexion pour parcourir les propriétés d'une Type et définir certains types à leurs valeurs par défaut. Maintenant, je pourrais changer le type et définir explicitement la default(Type), mais je préférerais le faire sur une seule ligne. Existe-t-il un équivalent programmatique de défaut?

462
tags2k
  • Dans le cas d'un type de valeur, utilisez Activator.CreateInstance et cela devrait fonctionner correctement.
  • Lorsque vous utilisez le type de référence, il suffit de renvoyer null
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

Dans la nouvelle version de .net telle que .net standard, type.IsValueType doit être écrit sous la forme type.GetTypeInfo().IsValueType

625
Dror Helper

Pourquoi ne pas appeler la méthode qui renvoie default (T) avec réflexion? Vous pouvez utiliser GetDefault de tout type avec:

    public object GetDefault(Type t)
    {
        return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
    }

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }
94
drake7707

Vous pouvez utiliser PropertyInfo.SetValue(obj, null). Si appelé sur un type de valeur, il vous donnera la valeur par défaut. Ce comportement est documenté dans .NET 4.0 et dans .NET 4.5

78
JoelFan

Si vous utilisez .NET 4.0 ou supérieur et que vous voulez une version programmatique qui ne soit pas une codification de règles définies en dehors du code, vous pouvez créer un Expression , le compiler et l'exécuter. -la mouche.

L'extension suivante utilise un Type et récupère la valeur renvoyée par default(T) via la méthode Default sur la classe Expression:

public static T GetDefaultValue<T>()
{
    // We want an Func<T> which returns the default.
    // Create that expression here.
    Expression<Func<T>> e = Expression.Lambda<Func<T>>(
        // The default value, always get what the *code* tells us.
        Expression.Default(typeof(T))
    );

    // Compile and return the value.
    return e.Compile()();
}

public static object GetDefaultValue(this Type type)
{
    // Validate parameters.
    if (type == null) throw new ArgumentNullException("type");

    // We want an Func<object> which returns the default.
    // Create that expression here.
    Expression<Func<object>> e = Expression.Lambda<Func<object>>(
        // Have to convert to object.
        Expression.Convert(
            // The default value, always get what the *code* tells us.
            Expression.Default(type), typeof(object)
        )
    );

    // Compile and return the value.
    return e.Compile()();
}

Vous devez également mettre en cache la valeur ci-dessus en fonction de Type, mais sachez que si vous l'appelez pour un grand nombre d'instances Type, et ne l'utilisez pas constamment, la mémoire utilisée par le cache pourrait l'emporter sur les avantages.

54
casperOne

Pourquoi dites-vous que les génériques sont hors de propos?

    public static object GetDefault(Type t)
    {
        Func<object> f = GetDefault<object>;
        return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
    }

    private static T GetDefault<T>()
    {
        return default(T);
    }
37
Rob Fonseca-Ensor

C'est la solution optimisée de Flem:

using System.Collections.Concurrent;

namespace System
{
    public static class TypeExtension
    {
        //a thread-safe way to hold default instances created at run-time
        private static ConcurrentDictionary<Type, object> typeDefaults =
           new ConcurrentDictionary<Type, object>();

        public static object GetDefaultValue(this Type type)
        {
            return type.IsValueType
               ? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
               : null;
        }
    }
}
22
cuft

La réponse choisie est une bonne réponse, mais soyez prudent avec l'objet renvoyé.

string test = null;
string test2 = "";
if (test is string)
     Console.WriteLine("This will never be hit.");
if (test2 is string)
     Console.WriteLine("Always hit.");

Extrapoler ...

string test = GetDefault(typeof(string));
if (test is string)
     Console.WriteLine("This will never be hit.");
7
BSick7

Les expressions peuvent aider ici:

    private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();

    private object GetTypedNull(Type type)
    {
        Delegate func;
        if (!lambdasMap.TryGetValue(type, out func))
        {
            var body = Expression.Default(type);
            var lambda = Expression.Lambda(body);
            func = lambda.Compile();
            lambdasMap[type] = func;
        }
        return func.DynamicInvoke();
    }

Je n'ai pas testé cet extrait, mais je pense qu'il devrait produire des valeurs null "typées" pour les types de référence.

5
Konstantin Isaev

Vous ne trouvez rien de simple et d'élégant pour l'instant, mais j'ai une idée: si vous connaissez le type de propriété que vous souhaitez définir, vous pouvez écrire votre propre default(T). Il existe deux cas - T est un type de valeur et T est un type de référence. Vous pouvez le voir en vérifiant T.IsValueType. Si T est un type de référence, vous pouvez simplement le définir sur null. Si T est un type de valeur, il aura un constructeur par défaut sans paramètre que vous pouvez appeler pour obtenir une valeur "vide".

3
Vilx-

Je fais la même tâche comme ça. 

//in MessageHeader 
   private void SetValuesDefault()
   {
        MessageHeader header = this;             
        Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
   }

//in ObjectPropertyHelper
   public static void SetPropertiesToDefault<T>(T obj) 
   {
            Type objectType = typeof(T);

            System.Reflection.PropertyInfo [] props = objectType.GetProperties();

            foreach (System.Reflection.PropertyInfo property in props)
            {
                if (property.CanWrite)
                {
                    string propertyName = property.Name;
                    Type propertyType = property.PropertyType;

                    object value = TypeHelper.DefaultForType(propertyType);
                    property.SetValue(obj, value, null);
                }
            }
    }

//in TypeHelper
    public static object DefaultForType(Type targetType)
    {
        return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
    }
3
kpollock

Équivalent à la réponse de Dror mais comme méthode d'extension:

namespace System
{
    public static class TypeExtensions
    {
        public static object Default(this Type type)
        {
            object output = null;

            if (type.IsValueType)
            {
                output = Activator.CreateInstance(type);
            }

            return output;
        }
    }
}
2
Paul Fleming

Légères modifications de la solution @Rob Fonseca-Ensor : La méthode d'extension suivante fonctionne également sur .Net Standard puisque j'utilise GetRuntimeMethod au lieu de GetMethod.

public static class TypeExtensions
{
    public static object GetDefault(this Type t)
    {
        var defaultValue = typeof(TypeExtensions)
            .GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
            .MakeGenericMethod(t).Invoke(null, null);
        return defaultValue;
    }

    public static T GetDefaultGeneric<T>()
    {
        return default(T);
    }
}

... et le test unitaire correspondant pour ceux qui se soucient de la qualité:

[Fact]
public void GetDefaultTest()
{
    // Arrange
    var type = typeof(DateTime);

    // Act
    var defaultValue = type.GetDefault();

    // Assert
    defaultValue.Should().Be(default(DateTime));
}
0
thomasgalliker
 /// <summary>
    /// returns the default value of a specified type
    /// </summary>
    /// <param name="type"></param>
    public static object GetDefault(this Type type)
    {
        return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
    }
0
Kaz-LA