Программный эквивалент значения по умолчанию (Тип)

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

Это должно работать: Nullable <T> a = new Nullable <T> () .GetValueOrDefault ();

dancer42 05.06.2020 05:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
547
1
147 492
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Пока не могу найти ничего простого и элегантного, но у меня есть одна идея: если вы знаете тип свойства, которое хотите установить, вы можете написать свой собственный default(T). Есть два случая: T - это тип значения, а T - это ссылочный тип. Вы можете убедиться в этом, проверив T.IsValueType. Если T является эталонным типом, вы можете просто установить его на null. Если T является типом значения, тогда он будет иметь конструктор без параметров по умолчанию, который вы можете вызвать, чтобы получить «пустое» значение.

Проголосовали против, потому что вопрос не в том, что у вас есть параметр типа T, а у вас есть экземпляр Type.

Chris Bordeman 09.12.2020 01:27

Я делаю ту же задачу вот так.

//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;
    }
Ответ принят как подходящий
  • В случае типа значения используйте Activator.CreateInstance, и он должен работать нормально.
  • При использовании ссылочного типа просто верните null
public static object GetDefault(Type type)
{
   if (type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

В более новой версии .net, такой как стандарт .net, type.IsValueType должен быть записан как type.GetTypeInfo().IsValueType.

Это вернет тип значения в штучной упаковке и, следовательно, не является точным эквивалентом значения по умолчанию (Тип). Тем не менее, это так близко, как вы собираетесь получить без дженериков.

Russell Giddings 07.07.2011 18:27

Ну и что? Если вы найдете тип, который default(T) != (T)(object)default(T) && !(default(T) != default(T)), то у вас есть аргумент, в противном случае не имеет значения, упакован он в коробку или нет, поскольку они эквивалентны.

Miguel Angelo 11.10.2012 10:14

Последняя часть предиката - избежать жульничества с перегрузкой оператора ... можно заставить default(T) != default(T) возвращать false, и это жульничество! знак равно

Miguel Angelo 11.10.2012 10:16

Это мне очень помогло, но я подумал, что должен добавить одну вещь, которая может быть полезна некоторым людям, ищущим этот вопрос - есть также эквивалентный метод, если вам нужен множество данного типа, и вы можете получить его с помощью Array.CreateInstance(type, length).

Darrel Hoffman 30.06.2013 05:34

Вы не беспокоитесь о создании экземпляра неизвестного типа значения? Это может иметь побочные эффекты.

ygormutti 14.10.2013 13:08

Если определение настраиваемых конструкторов без параметров для структур разрешено (возможно, в C# 6), это перестанет быть правильным решением.

IS4 26.04.2015 22:21

Это не работает для System.Net.IPAddress, потому что этот класс не предоставляет конструктор без параметров. default(IPAddress), однако, работает и является IPAddress.None. Не знаю, как это сделать с типом времени выполнения.

Jannes 21.10.2015 13:20

Если вы выполняете сравнение на равенство результата этого метода для типа на основе Enum, обратите внимание, что вы не можете использовать обычный оператор равенства ==. См. этот вопрос для получения дополнительной информации.

Jax 16.12.2015 19:36

Просто примечание (к этому ответу 7 (!) Лет назад): использование Activator.CreateInstance намного медленнее, чем использование отражения с GetConstructor для хранения ссылки на функцию конструктора любого типа значения (для повторного использования). Скрыт он или нет (как в примере @Jannes) не имеет значения. С дженериками это немного больше, но все же намного быстрее. Конечно, чаще всего люди, использующие рефлексию, не ждут скорости, но все же.

Abel 19.12.2015 02:10

Type.IsValueType был удален в DNX 5.0. Решение от drake7707 по-прежнему будет работать.

IMLiviu 22.01.2016 09:20

@Jannes, почему бы и нет? IPAddress является ссылочным типом и, следовательно, должен просто возвращать ноль?

nawfal 01.07.2016 20:37

Если вы собираетесь использовать этот код для возврата значения по умолчанию из метода (скажем, вы реализуете DispatchProxy, помните о правильной обработке методов возврата void. Для методов возврата void вы также должны вернуть null.

csharpfolk 23.12.2018 12:19

Не работает. Он возвращает null вместо Enum? тип нуля

Nick 20.01.2020 15:39

Для ссылочных типов это вернет значение NULL типа «объект», а не значение NULL типа «тип». Если вам нужен null для указанного объекта Type, верните Convert.ChangeType (null, type) вместо того, чтобы просто возвращать null.

dynamichael 22.06.2020 11:36

Выбранный ответ - хороший ответ, но будьте осторожны с возвращаемым объектом.

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), как и для любого другого ссылочного типа ...

TDaver 21.01.2011 17:12

Строка - это странная птица - это тип значения, который также может возвращать null. Если вы хотите, чтобы код возвращал string.empty, просто добавьте для него специальный случай

Dror Helper 07.07.2011 11:09

@Dror - строка является неизменяемым ссылочным типом, а не типом значения.

ljs 18.08.2011 20:01

@kronoz Вы правы - я имел в виду, что строка может обрабатываться, возвращая string.empty или null в зависимости от необходимости.

Dror Helper 21.08.2011 10:40

Почему вы говорите, что дженерики не используются?

    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.

Cœur 14.06.2014 17:43

насколько дорого обходится создание универсального метода во время выполнения, а затем использование его несколько тысяч раз подряд?

C. Tewalt 03.09.2014 21:11

Я думал о чем-то подобном. Лучшее и самое элегантное решение для меня. Работает даже на Compact Framework 2.0. Если вас беспокоит производительность, вы всегда можете кэшировать общий метод, не так ли?

Bart 27.10.2015 23:26

Это решение идеально подходит! Спасибо!

Lachezar Lalov 12.10.2018 16:12

Почему бы не вызвать метод, который возвращает значение по умолчанию (T) с отражением? Вы можете использовать GetDefault любого типа с:

    public object GetDefault(Type t)
    {
        return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
    }

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }

Это великолепно, потому что это так просто. Хотя здесь это не лучшее решение, его важно помнить, потому что этот метод может быть полезен во многих аналогичных обстоятельствах.

configurator 17.02.2013 16:45

Если вместо этого вы вызываете общий метод «GetDefault» (перегрузка), сделайте следующее: this.GetType (). GetMethod («GetDefault», новый тип [0]). <AS_IS>

Stefan Steiger 07.05.2013 14:50

Пусть GetDefaultGeneric вернет object, и вы готовы кэшировать для повышения скорости.

IS4 26.04.2015 22:24

Имейте в виду, что эта реализация намного медленнее (из-за отражения), чем принятый ответ. Он по-прежнему жизнеспособен, но вам нужно настроить кеширование для вызовов GetMethod () / MakeGenericMethod (), чтобы повысить производительность.

Doug 02.11.2015 19:33

Возможно, что аргумент типа недействителен. Например. MethodBase.ResultType () метода void вернет объект Type с именем «Void» или с полным именем «System.Void». Поэтому ставлю охрану: if (t.FullName == "System.Void") return null; Спасибо за решение.

Valo 23.03.2017 05:24

очень умно, и это работает в ядре .net, а также по сравнению с принятым ответом

Atul Chaudhary 05.06.2017 08:04

Лучше используйте nameof(GetDefaultGeneric), если можете, вместо "GetDefaultGeneric"

Mugen 10.10.2017 13:42

Здесь могут помочь выражения:

    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.
ToolmakerSteve 06.02.2018 05:57

Вы можете использовать PropertyInfo.SetValue(obj, null). Если вызывается для типа значения, он даст вам значение по умолчанию. Это поведение задокументировано в .NET 4.0 и в .NET 4.5.

Для этого конкретного вопроса - зацикливание свойств типа И установка для них значения «по умолчанию» - это работает блестяще. Я использую его при преобразовании из SqlDataReader в объект с помощью отражения.

Arno Peters 23.06.2013 13:13

Эквивалентно ответу 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 31.08.2014 23:48

@Cyrus А для значения, которое кешируется?

casperOne 02.09.2014 06:24

@Cyrus Я почти уверен, что все будет наоборот, если вы кэшируете e.Compile(). В этом весь смысл выражений.

nawfal 05.07.2016 11:38

Использование его с кешем для типов - imho лучшее решение!

BendEg 04.08.2017 10:44

Провел тест. Очевидно, что результат e.Compile() следует кэшировать, но если предположить, что этот метод примерно в 14 раз быстрее, например, для long. См. Тест и результаты gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b.

Pieter van Ginkel 30.01.2018 16:59

Re: «Если ... тогда память, потребляемая кешем, может перевесить преимущества». Решением в этом случае будет кеш LRU, например stackoverflow.com/a/3719378/199364 Также можно добавить метод «Очистить», который вы вызываете из любого имеющегося у вас метода очистки или нехватки памяти.

ToolmakerSteve 06.02.2018 06:28

Ради интереса, почему кэшировать e.Compile(), а не e.Compile()()? т.е. может ли тип по умолчанию измениться во время выполнения? Если нет (как я считаю), вы можете просто сохранить в кеше результат, а не скомпилированное выражение, что должно еще больше повысить производительность.

JohnLBevan 02.10.2018 16:19

@JohnLBevan - да, и тогда не имеет значения, какой метод вы используете для получения результата - все будут иметь чрезвычайно быструю амортизируемую производительность (поиск по словарю).

Daniel Earwicker 02.10.2018 16:36

Это оптимизированное решение Флема:

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;

Mark Whitfeld 23.01.2013 18:08

А как насчет изменяемых структур? Знаете ли вы, что можно (и законно) изменять поля структуры в штучной упаковке, чтобы данные изменялись?

IS4 26.04.2015 22:23

@ IllidanS4, поскольку название метода подразумевает, что это только для значений ValueType по умолчанию.

aderesh 24.01.2018 19:21
 /// <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. Принятый ответ Дрором работает лучше.

Cœur 16.06.2014 04:11

Небольшие изменения в Решение @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

Roman Badiornyi 23.12.2020 16:30

Другие вопросы по теме