Как проверить, является ли объект допустимым?

Как проверить, является ли данный объект нулевым, другими словами, как реализовать следующий метод...

bool IsNullableValueType(object o)
{
    ...
}

EDIT: Я ищу типы значений с нулевым значением. У меня не было типов ссылок.

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

obj теперь относится к объекту типа bool (System.Boolean) со значением, равным true. То, что я действительно хотел, было объектом типа Nullable<bool>

Итак, теперь, когда я работаю, я решил проверить, является ли o нулевым и создать обнуляемую оболочку вокруг объекта obj.

Ответ 1

Существует два типа обнуляемых - Nullable<T> и ссылочный тип.

Джон исправил меня, что трудно получить тип в штучной упаковке, но вы можете сделать это с помощью дженериков:  - так как насчет ниже. На самом деле это тестирование типа T, но использование параметра obj исключительно для вывода обобщенного типа (чтобы его было легко вызвать) - хотя он будет работать почти идентично без параметра obj.

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

Но это не сработает так хорошо, если вы уже поместили значение в переменную объекта.

Документация Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type

Ответ 2

Существует очень простое решение с использованием перегрузок методов

http://deanchalk.com/is-it-nullable/

выдержка:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

затем

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true

Ответ 3

Вопрос "Как проверить, является ли тип допустимым?" на самом деле "Как проверить, есть ли тип Nullable<>?", который можно обобщить на "Как проверить, является ли тип построенным типом некоторого родового типа?", так что он не только отвечает на вопрос "Is Nullable<int> a Nullable<>?", но также "Is List<int> a List<>?".

В большинстве представленных решений используется метод Nullable.GetUnderlyingType(), который, очевидно, будет работать только со случаем Nullable<>. Я не видел общего рефлексивного решения, которое будет работать с любым родовым типом, поэтому я решил добавить его здесь для потомков, хотя этот вопрос уже давно дан ответ.

Чтобы проверить, является ли тип некоторой формой Nullable<> с использованием отражения, сначала нужно преобразовать построенный общий тип, например Nullable<int>, в определение общего типа Nullable<>. Вы можете сделать это с помощью метода GetGenericTypeDefinition() класса Type. Затем вы можете сравнить полученный результат с Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

То же самое можно применить к любому родовому типу:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Несколько типов могут казаться одинаковыми, но другое количество аргументов типа означает, что это совершенно другой тип.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

Так как объект Type создается экземпляром один раз для каждого типа, вы можете проверить ссылочное равенство между ними. Поэтому, если вы хотите проверить, имеют ли два объекта одинаковое определение общего типа, вы можете написать:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

Если вы хотите проверить, является ли объект нулевым, а не Type, то вы можете использовать вышеупомянутую технику вместе с решением Marc Gravell для создания довольно простого метода:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

Ответ 4

Это работает для меня и кажется простым:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Ответ 5

Ну, вы могли бы использовать:

return !(o is ValueType);

... но сам объект не имеет значения NULL или иначе - тип. Как вы планировали использовать это?

Ответ 6

Самый простой способ выяснить:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

Ответ 7

Здесь есть две проблемы: 1) тестирование, чтобы определить, является ли Тип допустимым; и 2) тестирование, чтобы увидеть, представляет ли объект тип с нулевым значением.

Для проблемы 1 (тестирование типа), вот решение, которое я использовал в своих системах: TypeIsNullable-check solution

Для проблемы 2 (тестирование объекта) решение Dean Chalk выше работает для типов значений, но оно не работает для ссылочных типов, поскольку использование <T> overload всегда возвращает false. Поскольку ссылочные типы по своей сути являются нулевыми, тестирование ссылочного типа должно всегда возвращать значение true. Подробнее об этой семантике см. Примечание [О "nullability" ]. Таким образом, здесь моя модификация подхода Дина:

    public static bool IsObjectNullable<T>(T obj)
    {
        // If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
        if (!typeof(T).IsValueType || obj == null)
            return true;

        // Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
        return false; 
    }

    public static bool IsObjectNullable<T>(T? obj) where T : struct
    {
        // Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
        return true;
    }

И вот моя модификация к клиент-тестовому коду для вышеупомянутого решения:

    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    string g = "something";

    bool isnullable = IsObjectNullable(a); // false 
    isnullable = IsObjectNullable(b); // true 
    isnullable = IsObjectNullable(c); // true 
    isnullable = IsObjectNullable(d); // true 
    isnullable = IsObjectNullable(e); // true 
    isnullable = IsObjectNullable(f); // true 
    isnullable = IsObjectNullable(g); // true

Причина, по которой я изменил подход Дина в IsObjectNullable <T> (T t), состоит в том, что его исходный подход всегда возвращал false для ссылочного типа. Поскольку такой метод, как IsObjectNullable, должен иметь возможность обрабатывать значения ссылочного типа, и поскольку все ссылочные типы по своей сути являются обнуляемыми, то, если передан либо ссылочный тип, либо нуль, метод всегда должен возвращать true.

Вышеуказанные два метода можно заменить следующим единственным методом и получить тот же результат:

    public static bool IsObjectNullable<T>(T obj)
    {
        Type argType = typeof(T);
        if (!argType.IsValueType || obj == null)
            return true;
        return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

Однако проблема с этим последним однокомпонентным подходом заключается в том, что производительность страдает, когда Nullable <T> параметр используется. Требуется намного больше времени процессора для выполнения последней строки этого единственного метода, чем это позволяет компилятору выбрать вторую перегрузку метода, показанную ранее, когда в вызове IsObjectNullable используется параметр Nullable <T> type. Поэтому оптимальным решением является использование описанного здесь двухкомпонентного подхода.

CAVEAT: этот метод работает надежно, только если он вызван с использованием исходной ссылки на объект или точной копии, как показано в примерах. Однако, если объект с нулевым значением помещается в другой тип (например, объект и т.д.) Вместо того, чтобы оставаться в его исходной форме Nullable < > , этот метод не будет работать надежно. Если код, вызывающий этот метод, не использует исходную ссылку на unboxed object или точную копию, он не может надежно определить обнуление объекта с помощью этого метода.

В большинстве сценариев кодирования для определения нулевой вероятности нужно вместо этого полагаться на тестирование исходного типа объекта, а не на его ссылку (например, код должен иметь доступ к исходному типу объекта для определения нулевой вероятности). В этих более распространенных случаях IsTypeNullable (см. Ссылку) является надежным методом определения нулевой вероятности.

P.S. - Об "недействительности"

Я должен повторить высказывание о nullability, которое я сделал в отдельном сообщении, которое применяется непосредственно для правильного решения этой темы. То есть, я считаю, что основное внимание в обсуждении здесь не должно быть, как проверить, является ли объект общим типом Nullable, а скорее можно ли присвоить значение null объекту его типа. Другими словами, я думаю, мы должны определить, является ли тип объекта нулевым, а не является ли он Nullable. Разница заключается в семантике, а именно в практических причинах определения неопределенности, что обычно имеет значение.

В системе, использующей объекты с типами, которые неизвестны до времени выполнения (веб-службы, удаленные вызовы, базы данных, каналы и т.д.), общее требование состоит в том, чтобы определить, можно ли присвоить нуль объекту или объект может содержать нуль. Выполнение таких операций с непустыми типами может привести к ошибкам, обычно исключениям, которые очень дороги как с точки зрения производительности, так и с требованиями кодирования. Чтобы принять крайне предпочтительный подход к проактивному устранению таких проблем, необходимо определить, способен ли объект произвольного типа содержать нуль; то есть, является ли оно вообще "нулевым".

В очень практичном и типичном смысле неопределенность в .NET-терминах вовсе не обязательно означает, что объект Type является формой Nullable. Во многих случаях на самом деле объекты имеют ссылочные типы, могут содержать нулевое значение и, следовательно, все они могут быть обнуляемы; ни один из них не имеет типа Nullable. Поэтому для практических целей в большинстве сценариев тестирование должно проводиться для общей концепции обнуляемости по сравнению с концепцией Nullable, зависящей от реализации. Поэтому мы не должны зависеть, сосредотачиваясь исключительно на типе .NET Nullable, но скорее включаем наше понимание его требований и поведения в процессе фокусировки на общей практической концепции обнуления.

Ответ 8

Самое простое решение, которое я придумал, - это реализовать решение Microsoft (Как: определить тип Nullable (Руководство по программированию в С#)) в качестве метода расширения:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Затем это можно назвать так:

bool isNullable = typeof(int).IsNullable();

Это также кажется логичным способом доступа к IsNullable(), поскольку он подходит ко всем другим методам IsXxxx() класса Type.

Ответ 9

Будьте внимательны при боксировании типа с нулевым значением (Nullable<int> или int?):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

Он становится истинным ссылочным типом, поэтому вы теряете тот факт, что он был нулевым.

Ответ 10

Может быть, немного не по теме, но все же какая-то интересная информация. Я нахожу много людей, которые используют Nullable.GetUnderlyingType() != null для идентификации, если тип является нулевым. Это очевидно работает, но Microsoft советует следующее type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) (см. http://msdn.microsoft.com/en-us/library/ms366789.aspx).

Я посмотрел на это с точки зрения производительности. Вывод теста (один миллион попыток) ниже заключается в том, что когда тип является нулевым, опция Microsoft обеспечивает лучшую производительность.

Nullable.GetUnderlyingType(): 1335ms (в 3 раза медленнее)

GetGenericTypeDefinition() == typeof (Nullable < > ): 500 мс

Я знаю, что мы говорим о небольшом количестве времени, но все любят настраивать миллисекунды:-)! Итак, если вы босс хочет, чтобы вы сократили миллисекунды, тогда это ваш спаситель...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}

Ответ 11

Эта версия:

  • результаты кэширования быстрее,
  • не требует ненужных переменных, таких как Method (T obj)
  • НЕ СООТВЕТСТВУЕТ:),
  • просто статический универсальный класс, который имеет однократно вычисленные поля

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;

Ответ 12

Вот что я придумал, поскольку все остальное, казалось, потерпело неудачу - по крайней мере, на ПЛК - Portable Class Library/.NET Core s >= С# 6

Решение: Расширение статических методов для любых типов T и Nullable<T> и использование того факта, что статический метод расширения, соответствующий базовому типу, будет вызываться и имеет приоритет над общим T метод расширения.

Для T:

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

и для Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

Использование Reflection и type.IsGenericType... не работало с моим текущим набором .NET Runtimes. Также не помогла Документация MSDN.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

Отчасти потому, что API-интерфейс Reflection был значительно изменен в .NET Core.

Ответ 13

Я думаю, что те, которые используют предложенное Microsoft тестирование на IsGenericType, хороши, но в коде для GetUnderlyingType Microsoft использует дополнительный тест, чтобы убедиться, что вы не передали определение универсального типа Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));

Ответ 14

простой способ сделать это:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

это мои модульные тесты и все прошедшие

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

фактические модульные тесты

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }