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