Как Получить Значение Типа по Умолчанию во Время Выполнения
При написании кода можно использовать значение default(T). Но что, если вы хотите сделать то же самое во время выполнения? Как получить значение по умолчанию для любого System.Type? (Допустим, вам это понадобилось)
В сети можно найти множество неправильных ответов. Например, этот:
object? GetDefaultValue(Type t) =>Это отлично работает для ссылочных типов и большинства типов значений. Но Activator.CreateInstance(T) — это не то же самое, что default(T), когда T является типом значения. Activator.CreateInstance(T) аналогичен new T(). То есть, он вызывает конструктор по умолчанию. default(T) не вызывает конструктор по умолчанию. Он инициализирует все поля значениями по умолчанию.
t.IsValueType
? Activator.CreateInstance(t)
: null;
В C# 10 вы можете создать структуру с конструктором по умолчанию:
struct SampleПосмотрим на разницу между default(Sample) и new Sample():
{
public int Value { get; }
public Sample() => Value = 1;
}
default(Sample).Value // 0Так как же получить default(T) во время выполнения?
new Sample().Value // 1
Activator
.CreateInstance<Sample>()
.Value // 1
1. Использовать System.Linq.Expression, но это медленно (~20мс):
object? GetDefaultValue(Type t)2. Обобщённый класс со статическим свойством, которое возвращает значение обобщённого типа по умолчанию. Это быстрее (~2мс):
{
if (t.IsValueType)
{
var defExpr = Expression.Default(t);
return Expression
.Lambda(defExpr)
.Compile()
.DynamicInvoke();
}
return null;
}
object? GetDefaultValue(Type t)3. Начиная с.NET Core, вы можете использовать System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject, чтобы получить неинициализированный экземпляр (~30нс):
{
if (t.IsValueType)
{
var defType = typeof(DefaultProvider<>)
.MakeGenericType(t);
return defType
.InvokeMember("Value",
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.GetProperty,
binder: null,
target: null,
args: null,
culture: null);
}
return null;
}
private static class DefaultProvider<T>
{
public static T? Value => default;
}
object? GetDefaultValue(Type t)Можно использовать любой из этих методов, чтобы получить значение по умолчанию:
{
if (t.IsValueType)
return RuntimeHelpers
.GetUninitializedObject(t);
return null;
}
((Sample)GetDefaultValue(typeof(Sample)))Источник: https://www.meziantou.net/get-the-default-value-of-a-type-at-runtime.htm
.Value // 0