Korzystam z odbicia, aby przeglądać Type
wł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ć Type
wł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.IsValueType
należ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ć Type
i uzyskać wartość zwracana z default(T)
pośrednictwem Default
metody w Expression
klasie:
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 Type
instancji, 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 null
wartość 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 - T
jest to typ wartości i T
jest typem referencyjnym. Możesz to zobaczyć, zaznaczając T.IsValueType
. Jeśli T
jest to typ odniesienia, możesz go po prostu ustawić na null
. Jeśli T
jest 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.