Equivalente programmatico di default (Tipo)


514

Sto usando il riflesso per scorrere Typele proprietà di un e impostare alcuni tipi sul loro valore predefinito. Ora, potrei fare un interruttore sul tipo e impostare default(Type)esplicitamente, ma preferirei farlo in una riga. Esiste un equivalente programmatico di default?


Dovrebbe funzionare: Nullable <T> a = new Nullable <T> () .GetValueOrDefault ();
ballerino42

Risposte:


694
  • Nel caso di un tipo di valore, utilizzare Activator.CreateInstance e dovrebbe funzionare correttamente.
  • Quando si utilizza il tipo di riferimento, restituire null
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

Nella versione più recente di .net come .net standard, type.IsValueTypedeve essere scritto cometype.GetTypeInfo().IsValueType


22
Ciò restituirà un tipo di valore inscatolato e quindi non è l'equivalente esatto di default (Tipo). Tuttavia, è il più vicino possibile senza generici.
Russell Giddings,

8
E allora? Se trovi un tipo di cui default(T) != (T)(object)default(T) && !(default(T) != default(T))hai un argomento, altrimenti non importa se è inscatolato o meno, poiché sono equivalenti.
Miguel Angelo,

7
L'ultimo pezzo del predicato è evitare di barare con il sovraccarico dell'operatore ... si potrebbe rendere default(T) != default(T)falso il ritorno, e questo è barare! =)
Miguel Angelo,

4
Questo mi ha aiutato molto, ma ho pensato di aggiungere una cosa che potrebbe essere utile ad alcune persone che cercano questa domanda: esiste anche un metodo equivalente se si desidera una matrice di un determinato tipo e si può ottenere usando Array.CreateInstance(type, length).
Darrel Hoffman,

4
Non ti preoccupare di creare un'istanza di un tipo di valore sconosciuto? Ciò può avere effetti collaterali.
ygormutti,

103

Perché non chiamare il metodo che restituisce default (T) con reflection? Puoi utilizzare GetDefault di qualsiasi tipo con:

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

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

7
Questo è geniale perché è così semplice. Anche se non è la soluzione migliore qui, è una soluzione importante da tenere a mente perché questa tecnica può essere utile in molte circostanze simili.
configuratore

Se invece si chiama il metodo generico "GetDefault" (sovraccarico), procedere come segue: this.GetType (). GetMethod ("GetDefault", nuovo tipo [0]). <AS_IS>
Stefan Steiger

2
Tieni presente che questa implementazione è molto più lenta (a causa della riflessione) rispetto alla risposta accettata. È ancora praticabile, ma per migliorare le prestazioni dovresti impostare un po 'di memorizzazione nella cache per le chiamate GetMethod () / MakeGenericMethod ().
Doug,

1
È possibile che l'argomento type sia nullo. Ad esempio, MethodBase.ResultType () di un metodo void restituirà un oggetto Type con nome "Void" o con FullName "System.Void". Pertanto metto una guardia: if (t.FullName == "System.Void") restituisce null; Grazie per la soluzione
Valo,

8
Meglio usare nameof(GetDefaultGeneric)se puoi, invece di"GetDefaultGeneric"
Mugen,

87

È possibile utilizzare PropertyInfo.SetValue(obj, null). Se chiamato su un tipo di valore, questo ti darà il valore predefinito. Questo comportamento è documentato in .NET 4.0 e .NET 4.5 .


7
Per questa domanda specifica - il looping attraverso le proprietà di un tipo E impostandole su "default" - funziona alla grande. Lo uso durante la conversione da un SqlDataReader a un oggetto usando la riflessione.
Arno Peters,

57

Se si utilizza .NET 4.0 o versione successiva e si desidera una versione programmatica che non sia una codifica di regole definite al di fuori del codice , è possibile creare unExpression , compilare ed eseguirlo al volo.

Il seguente metodo di estensione prenderà a Typee otterrà il valore restituito default(T)tramite il Defaultmetodo sulla Expressionclasse:

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

Dovresti anche memorizzare nella cache il valore sopra basato su Type, ma fai attenzione se lo stai chiamando per un gran numero di Typeistanze e non utilizzarlo costantemente, la memoria consumata dalla cache potrebbe superare i vantaggi.


4
Prestazioni per "return type.IsValueType? Activator.CreateInstance (tipo): null; ' è 1000x più veloce di e.Compile () ();
Ciro,

1
@Cyrus Sono abbastanza sicuro che sarebbe il contrario se si memorizza nella cache e.Compile(). Questo è il punto centrale delle espressioni.
nawfal,

2
Ho superato un punto di riferimento. Ovviamente, il risultato di e.Compile()dovrebbe essere memorizzato nella cache, ma supponendo che questo metodo sia circa 14 volte più veloce per es long. Vedere gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b per il benchmark e i risultati.
Pieter van Ginkel,

3
Per interesse, perché cache e.Compile()anziché e.Compile()()? vale a dire che il tipo predefinito di un tipo può cambiare in fase di esecuzione? In caso contrario (come credo sia il caso) è possibile archiviare nella cache il risultato anziché l'espressione compilata, il che dovrebbe migliorare ulteriormente le prestazioni.
JohnLBevan,

3
@JohnLBevan - sì, e quindi non importa quale tecnica usi per ottenere il risultato - tutto avrà prestazioni ammortizzate estremamente veloci (una ricerca nel dizionario).
Daniel Earwicker,

38

Perché dici che i generici sono fuori dai giochi?

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

Impossibile risolvere il metodo Simbolo. Utilizzando un PCL per Windows.
Cœur

1
quanto costa creare il metodo generico in fase di esecuzione e quindi utilizzarlo diverse migliaia di volte di seguito?
C. Tewalt,

1
Stavo pensando a qualcosa del genere. La soluzione migliore e più elegante per me. Funziona anche su Compact Framework 2.0. Se sei preoccupato per le prestazioni, puoi sempre memorizzare nella cache un metodo generico, no?
Bart,

Questa soluzione si adatta esattamente! Grazie!
Lachezar Lalov,

25

Questa è la soluzione Flem ottimizzata:

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

2
Una versione breve del ritorno:return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
Mark Whitfeld il

3
Che dire delle strutture mutabili? Sai che è possibile (e legale) modificare i campi di una struttura scatolata, in modo che i dati cambino?
IllidanS4 vuole che Monica torni

@ IllidanS4 poiché il nome del metodo implica che questo è solo per i valori di ValueType predefiniti.
aderesh

8

La risposta scelta è una buona risposta, ma fai attenzione con l'oggetto restituito.

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

Estrapolando ...

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

14
vero, ma vale anche per default (stringa), come ogni altro tipo di riferimento ...
TDaver

string è un uccello dispari - essendo un tipo di valore che può anche restituire null. Se vuoi che il codice restituisca string.empty, aggiungi un caso speciale per esso
Dror Helper

15
@Dror - string è un tipo di riferimento immutabile, non un tipo di valore.
ljs,

@kronoz Hai ragione - intendevo dire che la stringa può essere gestita restituendo string.empty o null in base alle necessità.
Dror Helper,

5

Le espressioni possono aiutare qui:

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

Non ho testato questo frammento, ma penso che dovrebbe produrre valori null "tipizzati" per i tipi di riferimento.


1
"typed" nulls- spiegare. Quale oggetto stai restituendo? Se si restituisce un oggetto di tipo type, ma il suo valore è null, non ha - non può - avere altre informazioni diverse da quella che è null. Non è possibile eseguire una query su un nullvalore e scoprire di che tipo si tratta. Se NON restituisci null, ma restituisci .. Non so cosa .., allora non si comporterà null.
ToolmakerSteve

3

Non riesco ancora a trovare nulla di semplice ed elegante, ma ho un'idea: se conosci il tipo di proprietà che desideri impostare, puoi scriverne uno tuo default(T). Esistono due casi: Tè un tipo di valore ed Tè un tipo di riferimento. Puoi vederlo controllando T.IsValueType. Se Tè un tipo di riferimento, puoi semplicemente impostarlo su null. Se Tè un tipo di valore, avrà un costruttore senza parametri predefinito che puoi chiamare per ottenere un valore "vuoto".


3

Faccio lo stesso compito in questo modo.

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

2

Equivalente alla risposta di Dror ma come metodo di estensione:

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

Lievi aggiustamenti alla soluzione di @Rob Fonseca-Ensor : Il seguente metodo di estensione funziona anche su .Net Standard poiché utilizzo GetRuntimeMethod anziché 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);
    }
}

... e il test unitario corrispondente per coloro che hanno a cuore la qualità:

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

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

    // Assert
    defaultValue.Should().Be(default(DateTime));
}

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

2
Non funziona per i Nullable<T>tipi: non restituisce l'equivalente di default(Nullable<T>)quale dovrebbe essere null. La risposta accettata da Dror funziona meglio.
Cœur il

posso verificare se nullable usando la riflessione ...
ballerino42

0

Questo dovrebbe funzionare: Nullable<T> a = new Nullable<T>().GetValueOrDefault();

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.