Я использую отражение, чтобы перебрать Type
свойства и установить определенные типы по умолчанию. Теперь я мог бы переключить тип и установить default(Type)
явно, но я бы предпочел сделать это в одну строку. Есть ли программный эквивалент дефолта?
Я использую отражение, чтобы перебрать Type
свойства и установить определенные типы по умолчанию. Теперь я мог бы переключить тип и установить default(Type)
явно, но я бы предпочел сделать это в одну строку. Есть ли программный эквивалент дефолта?
Ответы:
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
В более новой версии .net, такой как стандарт .net, type.IsValueType
необходимо записать какtype.GetTypeInfo().IsValueType
default(T) != (T)(object)default(T) && !(default(T) != default(T))
есть аргумент, в противном случае не имеет значения, упакован он или нет, поскольку они эквивалентны.
default(T) != default(T)
возвращение ложным, и это обман! =)
Array.CreateInstance(type, length)
.
Почему бы не вызвать метод, который возвращает default (T) с отражением? Вы можете использовать GetDefault любого типа с:
public object GetDefault(Type t)
{
return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
}
public T GetDefaultGeneric<T>()
{
return default(T);
}
nameof(GetDefaultGeneric)
если вы можете, вместо"GetDefaultGeneric"
Вы можете использовать PropertyInfo.SetValue(obj, null)
. Если вызывается для типа значения, он даст вам значение по умолчанию. Это поведение описано в .NET 4.0 и .NET 4.5 .
Если вы используете .NET 4.0 или выше и вам нужна программная версия, которая не является кодификацией правил, определенных вне кода , вы можете создавать Expression
, компилировать и запускать ее на лету.
Следующий метод расширения будет принимать Type
и получить значение , возвращаемое default(T)
через Default
метод на Expression
классе:
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()();
}
Вы также должны кешировать вышеуказанное значение на основе Type
, но учтите, что если вы вызываете это для большого числа Type
экземпляров и не используете его постоянно, объем памяти, используемой кешем, может перевесить преимущества.
e.Compile()
. Вот и весь смысл выражений.
e.Compile()
должен быть кэширован, но при условии, что этот метод примерно в 14 раз быстрее, например long
. См. Gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b для оценки и результатов.
e.Compile()
а не e.Compile()()
? т.е. может ли тип по умолчанию изменить тип во время выполнения? Если нет (как я полагаю, так), вы можете просто сохранить кеш-результат, а не скомпилированное выражение, что должно еще больше повысить производительность.
Почему вы говорите, что дженерики за кадром?
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);
}
Это оптимизированное решение Флема:
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;
Выбранный ответ является хорошим ответом, но будьте осторожны с возвращенным объектом.
string test = null;
string test2 = "";
if (test is string)
Console.WriteLine("This will never be hit.");
if (test2 is string)
Console.WriteLine("Always hit.");
Экстраполяция ...
string test = GetDefault(typeof(string));
if (test is string)
Console.WriteLine("This will never be hit.");
Выражения могут помочь здесь:
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();
}
Я не тестировал этот фрагмент, но я думаю, что он должен выдавать «типизированные» нули для ссылочных типов.
"typed" nulls
объясните. Какой объект вы возвращаете? Если вы возвращаете объект типа type
, но его значение равно null
, тогда у него нет - не может - никакой другой информации, кроме той, которая есть null
. Вы не можете запросить null
значение и выяснить, какой тип он предположительно. Если вы НЕ вернете ноль, но вернетесь ... Я не знаю, что .., тогда это не будет вести себя как null
.
Пока не могу найти ничего простого и элегантного, но у меня есть одна идея: если вы знаете тип свойства, которое хотите установить, вы можете написать свой собственный default(T)
. Есть два случая - T
это тип значения и T
ссылочный тип. Вы можете увидеть это, проверив T.IsValueType
. Если T
это ссылочный тип, вы можете просто установить его null
. Если T
это тип значения, то у него будет конструктор по умолчанию без параметров, который можно вызвать, чтобы получить «пустое» значение.
Я делаю ту же задачу, как это.
//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;
}
Эквивалентно ответу Дрора, но как метод расширения:
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;
}
}
}
Небольшие изменения в решении @Rob Fonseca-Ensor : следующий метод расширения также работает на .Net Standard, так как я использую GetRuntimeMethod вместо 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);
}
}
... и соответствующий юнит-тест для тех, кто заботится о качестве:
[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>
типов: он не возвращает эквивалент, default(Nullable<T>)
который должен быть null
. Принятый ответ от Дрор работает лучше.