web-dev-qa-db-de.com

Programmatisches Äquivalent zum Standard (Typ)

Ich verwende Reflection, um die Eigenschaften eines Type zu durchlaufen und bestimmte Typen auf ihre Standardwerte zu setzen. Jetzt könnte ich den Typ umschalten und explizit die default(Type) setzen, aber ich würde es lieber in einer Zeile machen. Gibt es ein programmatisches Äquivalent von Default?

462
tags2k
  • Im Falle eines Werttyps benutze Activator.CreateInstance und es sollte gut funktionieren.
  • Wenn Sie einen Referenztyp verwenden, geben Sie einfach null zurück
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

In der neueren Version von .net, z. B. .net-Standard, muss type.IsValueType als type.GetTypeInfo().IsValueType geschrieben werden.

625
Dror Helper

Warum rufen Sie nicht die Methode auf, die default (T) mit Reflektion zurückgibt? Sie können GetDefault von jedem Typ verwenden mit:

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

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

Sie können PropertyInfo.SetValue(obj, null) verwenden. Wenn für einen Werttyp aufgerufen, erhalten Sie den Standardwert. Dieses Verhalten ist dokumentiert in .NET 4.0 und in .NET 4.5

78
JoelFan

Wenn Sie .NET 4.0 oder höher verwenden und eine programmgesteuerte Version wünschen, die keine Kodierung der definierten Regeln außerhalb von Code ist, können Sie eine Expression erstellen, kompilieren und on-the ausführen -fliegen.

Die folgende Erweiterungsmethode nimmt eine Type und erhält den Wert von default(T) über die Default-Methode in der Expression-Klasse:

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

Sie sollten den obigen Wert auch auf der Grundlage der Variablen Type zwischenspeichern. Wenn Sie dies jedoch für eine große Anzahl von Type-Instanzen aufrufen, sollten Sie sich dessen bewusst sein, dass der von dem Cache belegte Speicher die Vorteile bei weitem übersteigt.

54
casperOne

Warum sagen Sie, dass Generika nicht im Bild sind?

    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

Dies ist die Lösung von 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

Die gewählte Antwort ist eine gute Antwort, aber seien Sie vorsichtig mit dem zurückgegebenen Objekt.

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

Extrapolieren ...

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

Die Ausdrücke können hier helfen:

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

Ich habe dieses Snippet nicht getestet, aber ich denke, es sollte "typisierte" Nullen für Referenztypen erzeugen.

5

Ich kann noch nichts einfaches und elegantes finden, aber ich habe eine Idee: Wenn Sie den Typ der Eigenschaft kennen, die Sie einstellen möchten, können Sie Ihre eigene default(T) schreiben. Es gibt zwei Fälle - T ist ein Werttyp und T ist ein Referenztyp. Sie können dies sehen, indem Sie T.IsValueType überprüfen. Wenn T ein Referenztyp ist, können Sie ihn einfach auf null setzen. Wenn T ein Werttyp ist, hat er einen parameterlosen Standardkonstruktor, den Sie aufrufen können, um einen "leeren" Wert zu erhalten.

3
Vilx-

Ich mache die gleiche Aufgabe wie diese. 

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

Entspricht Drors Antwort, aber als Erweiterungsmethode:

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

Leichte Anpassungen an @Rob Fonseca-Ensors Lösung : Die folgende Erweiterungsmethode funktioniert auch für .Net Standard, da ich GetRuntimeMethod anstelle von GetMethod verwende.

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

... und der entsprechende Unit-Test für alle, die Wert auf Qualität legen:

[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