Я использую отражение, чтобы просмотреть свойства Type и установить для определенных типов значения по умолчанию. Теперь я мог бы переключить тип и явно указать default(Type), но я бы предпочел сделать это одной строкой. Есть ли программный эквивалент значения по умолчанию?





Пока не могу найти ничего простого и элегантного, но у меня есть одна идея: если вы знаете тип свойства, которое хотите установить, вы можете написать свой собственный default(T). Есть два случая: T - это тип значения, а T - это ссылочный тип. Вы можете убедиться в этом, проверив T.IsValueType. Если T является эталонным типом, вы можете просто установить его на null. Если T является типом значения, тогда он будет иметь конструктор без параметров по умолчанию, который вы можете вызвать, чтобы получить «пустое» значение.
Проголосовали против, потому что вопрос не в том, что у вас есть параметр типа T, а у вас есть экземпляр Type.
Я делаю ту же задачу вот так.
//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;
}
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) возвращать false, и это жульничество! знак равно
Это мне очень помогло, но я подумал, что должен добавить одну вещь, которая может быть полезна некоторым людям, ищущим этот вопрос - есть также эквивалентный метод, если вам нужен множество данного типа, и вы можете получить его с помощью Array.CreateInstance(type, length).
Вы не беспокоитесь о создании экземпляра неизвестного типа значения? Это может иметь побочные эффекты.
Если определение настраиваемых конструкторов без параметров для структур разрешено (возможно, в C# 6), это перестанет быть правильным решением.
Это не работает для System.Net.IPAddress, потому что этот класс не предоставляет конструктор без параметров. default(IPAddress), однако, работает и является IPAddress.None. Не знаю, как это сделать с типом времени выполнения.
Если вы выполняете сравнение на равенство результата этого метода для типа на основе Enum, обратите внимание, что вы не можете использовать обычный оператор равенства ==. См. этот вопрос для получения дополнительной информации.
Просто примечание (к этому ответу 7 (!) Лет назад): использование Activator.CreateInstance намного медленнее, чем использование отражения с GetConstructor для хранения ссылки на функцию конструктора любого типа значения (для повторного использования). Скрыт он или нет (как в примере @Jannes) не имеет значения. С дженериками это немного больше, но все же намного быстрее. Конечно, чаще всего люди, использующие рефлексию, не ждут скорости, но все же.
Type.IsValueType был удален в DNX 5.0. Решение от drake7707 по-прежнему будет работать.
@Jannes, почему бы и нет? IPAddress является ссылочным типом и, следовательно, должен просто возвращать ноль?
Если вы собираетесь использовать этот код для возврата значения по умолчанию из метода (скажем, вы реализуете DispatchProxy, помните о правильной обработке методов возврата void. Для методов возврата void вы также должны вернуть null.
Не работает. Он возвращает null вместо Enum? тип нуля
Для ссылочных типов это вернет значение NULL типа «объект», а не значение NULL типа «тип». Если вам нужен null для указанного объекта Type, верните Convert.ChangeType (null, type) вместо того, чтобы просто возвращать 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.");
true, но это справедливо и для default (string), как и для любого другого ссылочного типа ...
Строка - это странная птица - это тип значения, который также может возвращать null. Если вы хотите, чтобы код возвращал string.empty, просто добавьте для него специальный случай
@Dror - строка является неизменяемым ссылочным типом, а не типом значения.
@kronoz Вы правы - я имел в виду, что строка может обрабатываться, возвращая string.empty или null в зависимости от необходимости.
Почему вы говорите, что дженерики не используются?
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);
}
Невозможно разрешить метод символа. Использование PCL для Windows.
насколько дорого обходится создание универсального метода во время выполнения, а затем использование его несколько тысяч раз подряд?
Я думал о чем-то подобном. Лучшее и самое элегантное решение для меня. Работает даже на Compact Framework 2.0. Если вас беспокоит производительность, вы всегда можете кэшировать общий метод, не так ли?
Это решение идеально подходит! Спасибо!
Почему бы не вызвать метод, который возвращает значение по умолчанию (T) с отражением? Вы можете использовать GetDefault любого типа с:
public object GetDefault(Type t)
{
return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
}
public T GetDefaultGeneric<T>()
{
return default(T);
}
Это великолепно, потому что это так просто. Хотя здесь это не лучшее решение, его важно помнить, потому что этот метод может быть полезен во многих аналогичных обстоятельствах.
Если вместо этого вы вызываете общий метод «GetDefault» (перегрузка), сделайте следующее: this.GetType (). GetMethod («GetDefault», новый тип [0]). <AS_IS>
Пусть GetDefaultGeneric вернет object, и вы готовы кэшировать для повышения скорости.
Имейте в виду, что эта реализация намного медленнее (из-за отражения), чем принятый ответ. Он по-прежнему жизнеспособен, но вам нужно настроить кеширование для вызовов GetMethod () / MakeGenericMethod (), чтобы повысить производительность.
Возможно, что аргумент типа недействителен. Например. MethodBase.ResultType () метода void вернет объект Type с именем «Void» или с полным именем «System.Void». Поэтому ставлю охрану: if (t.FullName == "System.Void") return null; Спасибо за решение.
очень умно, и это работает в ядре .net, а также по сравнению с принятым ответом
Лучше используйте nameof(GetDefaultGeneric), если можете, вместо "GetDefaultGeneric"
Здесь могут помочь выражения:
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, а вернетесь .. Я не знаю что .., тогда он не будет действовать как null.
Вы можете использовать PropertyInfo.SetValue(obj, null). Если вызывается для типа значения, он даст вам значение по умолчанию. Это поведение задокументировано в .NET 4.0 и в .NET 4.5.
Для этого конкретного вопроса - зацикливание свойств типа И установка для них значения «по умолчанию» - это работает блестяще. Я использую его при преобразовании из SqlDataReader в объект с помощью отражения.
Эквивалентно ответу Dror, но как метод расширения:
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;
}
}
}
Если вы используете .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 и не используете его постоянно, память, потребляемая кешем, может перевесить преимущества.
Производительность для типа возврата. IsValueType? Activator.CreateInstance (тип): null; ' в 1000 раз быстрее, чем e.Compile () ();
@Cyrus А для значения, которое кешируется?
@Cyrus Я почти уверен, что все будет наоборот, если вы кэшируете e.Compile(). В этом весь смысл выражений.
Использование его с кешем для типов - imho лучшее решение!
Провел тест. Очевидно, что результат e.Compile() следует кэшировать, но если предположить, что этот метод примерно в 14 раз быстрее, например, для long. См. Тест и результаты gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b.
Re: «Если ... тогда память, потребляемая кешем, может перевесить преимущества». Решением в этом случае будет кеш LRU, например stackoverflow.com/a/3719378/199364 Также можно добавить метод «Очистить», который вы вызываете из любого имеющегося у вас метода очистки или нехватки памяти.
Ради интереса, почему кэшировать e.Compile(), а не e.Compile()()? т.е. может ли тип по умолчанию измениться во время выполнения? Если нет (как я считаю), вы можете просто сохранить в кеше результат, а не скомпилированное выражение, что должно еще больше повысить производительность.
@JohnLBevan - да, и тогда не имеет значения, какой метод вы используете для получения результата - все будут иметь чрезвычайно быструю амортизируемую производительность (поиск по словарю).
Это оптимизированное решение Флема:
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;
А как насчет изменяемых структур? Знаете ли вы, что можно (и законно) изменять поля структуры в штучной упаковке, чтобы данные изменялись?
@ IllidanS4, поскольку название метода подразумевает, что это только для значений ValueType по умолчанию.
/// <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. Принятый ответ Дрором работает лучше.
Небольшие изменения в Решение @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));
}
Это должно работать:
Nullable<T> a = new Nullable<T>().GetValueOrDefault();
он будет работать только для структуры, поскольку Nullable имеет общее ограничение на T: где T: struct
Это должно работать: Nullable <T> a = new Nullable <T> () .GetValueOrDefault ();