Страницы

Поиск по вопросам

среда, 27 ноября 2019 г.

Проверить, является ли значение объекта значением по умолчанию. C#

#c_sharp #.net #объекты #рефлексия


Update: В этом вопросе находятся реализации функции, представленные под этим вопросом.
Самая быстрая реализация V7

Необходимо написать функцию, которая принимает объект и возвращает имеет ли объект
значение по умолчанию или нет.

public bool IsDefault(object o)
{
    return ...
}


Сейчас единственное что я придумал, это узнавать имя типа и через switch проверять
имеет ли объект значение по умолчанию для типа. Выглядит это примерно так:

string typeName = o.GetType().Name;
switch (typeName)
{
    case "Int32":
        if(Convert.ToInt32(o) == 0)
        {
            return true;
        }
        return false;
    case "String":
        if (o == null || String.IsNullOrEmpty(Convert.ToString(o)))
        {
            return true;
        }
        return false;
        ...
        ...
        ...
}

    


Ответы

Ответ 1



Думаю самый лучший вариант вот: bool isDefault(T o) { return (o==null)?true: // считаем что null default o.GetType().IsValueType && !typeof(T).IsGenericType ? o.Equals(Activator.CreateInstance(o.GetType())): // default типа который внутри nullable o.Equals(default(T)); // настоящий default } Оказывается если идёт cast в object - то обьект немного "коробится". При этом может терятся информация о template (спорный вопрос с Nullable). Для "простых" типов я это учёл в втором условии. Но самый "правильный" вот bool isDefault(T o) { return (o==null)?(default(T)==null):o.Equals(default(T)); при этом isDefault((object)0) этот вариант даст false, т.к. defalut(object) != 0 что может не понравится разработчику, этот случй хороший только при использовании template. В зависимости от того работаете вы с template типом или кастите всё к object вам подходит тот или иной способ. Если вы хотите что б IsDefault((int?)0) было true - прийдётся чуть упростить (убрать IsGenericType). Случай default(T)==null бывает, но очень врядли default(T)!=null и o == null будет одновременно, поэтому вместо default(T)!=null можно писать true Остальные оставлю для истории... Через рефлексию (тут идёт Cast в object поэтому теряются некоторые значения, например IsDefault((int?)0) даст true class YourClass { public static T getDefault() { return default(T); } public bool IsDefault(object o){ if (o == null) return true; // условно object cmp = typeof(YourClass).GetMethod("getDefault") .MakeGenericMethod(new Type[]{ o.GetType() } ).Invoke(null,null); // теперь cmp содержит "дефолтное" значение bool equal = o.Equals(cmp); // дефолтное значение может быть null return equal; } Для этого нужно соединить два вопроса вот это https://stackoverflow.com/questions/1895761/test-for-equality-to-the-default-value соединить с вот этим https://stackoverflow.com/questions/6204326/calling-generic-method-using-reflection-in-net Если же вам тип заранее известен, то можно просто использовать generic без reflection так bool isObjDefault(object o) { return o.Equals(default(T)); } тут тоже теряется информация - IsDefault((int?)0) даст true, поэтому лучше даже так (оказывается так не нужно указывать тип, и обьект не конвертируется в object, из-за конвертации может теряется тип) bool isDefault(T o) { return (o==null)?true:o.Equals(default(T)); } Ну а если у вас два-три типа можно так (без generic, просто сравнение типа): if (o is int) return o.Equals(default(int)); if (o is string) return o.Equals(default(string)); Ещё необычный "короткий" вариант (с классами сработало с числами и строками тоже, правда этот вариант более затратный по ресурсам, зато в одну строчку) public bool IsDefault(object o) { return (o==null)?true:o.GetType().Assembly.CreateInstance(o.GetType().FullName).Equals(o); } но этот метод создаёт пустую іnstance если нету default значения, поэтому подходит в отдельных случаях. P.S. 1. Для классов default значение как правило null (случай когда не null в студию - не нашёл такого) в том числе и generic. Для valuetype и для struct существуют default значения. Очень часто в программах можно увидеть использования object для всех типов. При конветации в object могут терятся данные, например от Nullable<> остаётся либо исходный null без типа, либо int, а настоящий тип теряется. Поэтому можно записать две функции f1(x) != f2(x) там где есть конвертация будет не то значение.

Ответ 2



Мы не имеем права «терять» настоящий тип объекта, иначе будет невозможно отличить int? от int. Поэтому придётся делать вот такой вариант: bool IsDefault(T o) { if (o == null) // => ссылочный тип или nullable return true; if (Nullable.GetUnderlyingType(typeof(T)) != null) // nullable, не null return false; var type = o.GetType(); if (type.IsClass) return false; else // => тип-значение, есть конструктор по умолчанию return Activator.CreateInstance(type).Equals(o); } Проверка: Console.WriteLine(IsDefault(default(string))); // true Console.WriteLine(IsDefault(string.Empty)); // false Console.WriteLine(IsDefault(default(int))); // true Console.WriteLine(IsDefault(1)); // false Console.WriteLine(IsDefault(default(int?))); // true Console.WriteLine(IsDefault((int?)0)); // false Console.WriteLine(IsDefault((object)0)); // true Console.WriteLine(IsDefault((object)1)); // false На объект (object)(int?)0 метод выдаст true, т. к. переданный объект — упакованный int, а не упакованный int? (его не бывает в природе).

Ответ 3



Значение по умолчанию для типа можно получить так: public object GetDefaultValue(Type target) { Expression> e = Expression.Lambda>( Expression.Convert( Expression.Default(target), typeof(object))); return e.Compile()(); } Использование: public bool IsDefault(object o) { if(o == null) throw new ArgumentNullException(nameof(o)); return o.Equals(GetDefaultValue(o.GetType())); }

Ответ 4



На днях вышло обновление языка c# до версии 7.1 и теперь вы можете писать так: return (o is default); UPD Не будет работать, так как o типа object, а default(object) == null

Ответ 5



Ещё один вариант, который кажется мне наиболее быстрым. Воспользуемся трюком отсюда: class RequireStruct where T : struct { } class RequireClass where T : class { } static bool IsDefault(T o, RequireClass ignore = null) where T : class { if (o == null) return true; if (!(o is ValueType)) // не упакованная ли это структура? return false; // нет - выходим return Activator.CreateInstance(o.GetType()).Equals(o); // медленный путь } static bool IsDefault(T? o) where T : struct => o == null; static bool IsDefault(T o, RequireStruct ignore = null) where T : struct => default(T).Equals(o); // default(T) не требует рефлексии

Комментариев нет:

Отправить комментарий