Korzystam z odbicia, aby przeglądać Typewłaściwości a i ustawiać niektóre typy na domyślne. Teraz mógłbym zmienić typ i ustawić default(Type)jawnie, ale wolałbym to zrobić w jednym wierszu. Czy istnieje programowy odpowiednik wartości domyślnej?
Korzystam z odbicia, aby przeglądać Typewłaściwości a i ustawiać niektóre typy na domyślne. Teraz mógłbym zmienić typ i ustawić default(Type)jawnie, ale wolałbym to zrobić w jednym wierszu. Czy istnieje programowy odpowiednik wartości domyślnej?
Odpowiedzi:
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
W nowszej wersji .net, takiej jak .net standard, type.IsValueTypenależy zapisać jakotype.GetTypeInfo().IsValueType
default(T) != (T)(object)default(T) && !(default(T) != default(T))masz argument, w przeciwnym razie nie ma znaczenia, czy jest on zapakowany, czy nie, ponieważ są one równoważne.
default(T) != default(T)zwrot będzie fałszywy, a to jest oszustwo! =)
Array.CreateInstance(type, length).
Dlaczego nie wywołać metody, która zwraca wartość domyślną (T) z odbiciem? Możesz używać GetDefault dowolnego typu z:
public object GetDefault(Type t)
{
return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
}
public T GetDefaultGeneric<T>()
{
return default(T);
}
nameof(GetDefaultGeneric)jeśli możesz, zamiast"GetDefaultGeneric"
Możesz użyć PropertyInfo.SetValue(obj, null). Wywołanie typu wartości daje wartość domyślną. To zachowanie jest udokumentowane w .NET 4.0 i .NET 4.5 .
Jeśli używasz .NET 4.0 lub nowszej wersji i chcesz mieć wersję programową, która nie jest kodyfikacją reguł zdefiniowanych poza kodem , możesz utworzyć Expression, skompilować i uruchomić go w locie.
Poniższa metoda rozszerzenie będzie trwać Typei uzyskać wartość zwracana z default(T)pośrednictwem Defaultmetody w Expressionklasie:
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()();
}
Powinieneś również buforować powyższą wartość na podstawie Type, ale pamiętaj, jeśli wywołujesz to dla dużej liczby Typeinstancji, i nie używaj jej stale, pamięć zajęta przez pamięć podręczną może przeważyć korzyści.
e.Compile(). To jest cały punkt wyrażeń.
e.Compile()należy zapisać w pamięci podręcznej, ale przy założeniu, że metoda ta jest około 14 razy szybsza np long. Zobacz test i wyniki na gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b .
e.Compile()zamiast pamięci podręcznej e.Compile()()? tzn. Czy domyślny typ typu może się zmieniać w czasie wykonywania? Jeśli nie (jak sądzę), możesz po prostu zapisać wynik w pamięci podręcznej zamiast skompilowanego wyrażenia, co powinno jeszcze bardziej poprawić wydajność.
Dlaczego mówisz, że generyczne są poza obrazem?
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);
}
Jest to zoptymalizowane rozwiązanie 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;
}
}
}
return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
Wybrana odpowiedź jest dobrą odpowiedzią, ale uważaj na zwrócony obiekt.
string test = null;
string test2 = "";
if (test is string)
Console.WriteLine("This will never be hit.");
if (test2 is string)
Console.WriteLine("Always hit.");
Ekstrapolacja ...
string test = GetDefault(typeof(string));
if (test is string)
Console.WriteLine("This will never be hit.");
Wyrażenia mogą tutaj pomóc:
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();
}
Nie testowałem tego fragmentu, ale myślę, że powinien on wygenerować „typowane” wartości zerowe dla typów referencyjnych.
"typed" nulls- wyjaśnić. Jaki przedmiot zwracasz? Jeśli zwrócisz obiekt typu type, ale jego wartość to null, to nie będzie - nie może - mieć żadnych innych informacji poza tym, czym jest null. Nie możesz zapytać o nullwartość i dowiedzieć się, jaki to typ. Jeśli NIE zwrócisz null, ale wrócisz ... Nie wiem co ..., to nie będzie tak null.
Nie mogę jeszcze znaleźć niczego prostego i eleganckiego, ale mam jeden pomysł: jeśli znasz typ nieruchomości, którą chcesz ustawić, możesz napisać własną default(T). Istnieją dwa przypadki - Tjest to typ wartości i Tjest typem referencyjnym. Możesz to zobaczyć, zaznaczając T.IsValueType. Jeśli Tjest to typ odniesienia, możesz go po prostu ustawić na null. Jeśli Tjest typem wartości, będzie miał domyślny konstruktor bez parametrów, który można wywołać, aby uzyskać „pustą” wartość.
Wykonuję takie samo zadanie jak to.
//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;
}
Odpowiednik odpowiedzi Drora, ale jako metoda rozszerzenia:
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;
}
}
}
Nieznaczne zmiany w rozwiązaniu @Rob Fonseca-Ensor : Następująca metoda rozszerzenia działa również w .Net Standard, ponieważ używam GetRuntimeMethod zamiast 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);
}
}
... i odpowiedni test jednostkowy dla tych, którym zależy na jakości:
[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;
}
Nullable<T>typów: nie zwraca odpowiednika, default(Nullable<T>)który powinien być null. Odpowiedź zaakceptowana przez Drora działa lepiej.