Как проверить, является ли объект обнуляемым?
Как проверить, является ли данный объект обнуляемым, другими словами, как реализовать следующий метод...
bool IsNullableValueType(object o)
{
...
}
РЕДАКТИРОВАТЬ: Я ищу обнуляемые типы значений. Я не имел в виду типы ссылок.
//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);
}
объект теперь относится к объекту типа bool
(System.Boolean
) со значением, равным true
, То, что я действительно хотел, было объектом типа Nullable<bool>
Так что теперь, как обходной путь, я решил проверить, может ли o быть обнуляемым, и создать обнуляемую оболочку вокруг obj.
14 ответов
Есть два типа обнуляемых - 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
}
Но это не будет работать так хорошо, если вы уже поместили значение в переменную объекта.
Существует очень простое решение, использующее перегрузки методов.
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
Это работает для меня и кажется простым:
static bool IsNullable<T>(T obj)
{
return default(T) == null;
}
Вопрос "Как проверить, можно ли обнулять тип?" на самом деле "Как проверить, является ли тип Nullable<>
?", который можно обобщить на" Как проверить, является ли тип составным типом некоторого универсального типа?", чтобы он не только отвечал на вопрос" Является ли Nullable<int>
Nullable<>
?", но также" Есть List<int>
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
затем вы можете использовать вышеописанную технику вместе с решением Марка Гравелла, чтобы создать довольно простой метод:
static bool IsNullable<T>(T obj)
{
if (!typeof(T).IsGenericType)
return false;
return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
Ну, вы можете использовать:
return !(o is ValueType);
... но сам объект не может быть обнуляемым или нет - тип есть. Как вы планировали использовать это?
Самое простое решение, которое я придумал, - это реализовать решение Microsoft ( Как: определить тип Nullable (Руководство по программированию в C#)) в качестве метода расширения:
public static bool IsNullable(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>);
}
Это можно тогда назвать так:
bool isNullable = typeof(int).IsNullable();
Это также кажется логичным способом доступа IsNullable()
потому что это вписывается со всеми другими IsXxxx()
методы Type
учебный класс.
Самый простой способ понять это:
public bool IsNullable(object obj)
{
Type t = obj.GetType();
return t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
Здесь есть две проблемы: 1) тестирование, чтобы увидеть, является ли Тип обнуляемым; и 2) тестирование, чтобы увидеть, представляет ли объект обнуляемый тип.
Для выпуска 1 (тестирование типа) вот решение, которое я использовал в своих собственных системах: решение TypeIsNullable-check
Для проблемы 2 (тестирование объекта) решение Дина Чака, приведенное выше, работает для типов значений, но не работает для ссылочных типов, поскольку перегрузка
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
Вышеупомянутые два метода могут быть заменены следующим единственным методом и получить тот же результат:
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
CAVEAT: этот метод работает надежно, только если вызывается с использованием исходной ссылки на объект или точной копии, как показано в примерах. Однако, если обнуляемый объект помещается в другой тип (например, объект и т. Д.) Вместо того, чтобы оставаться в исходной форме Nullable<>, этот метод не будет работать надежно. Если код, вызывающий этот метод, не использует исходную, распакованную ссылку на объект или точную копию, он не может надежно определить обнуляемость объекта с помощью этого метода.
В большинстве сценариев кодирования для определения обнуляемости вместо этого нужно полагаться на тестирование Типа исходного объекта, а не его ссылки (например, код должен иметь доступ к исходному Типу объекта для определения обнуляемости). В этих более распространенных случаях IsTypeNullable (см. Ссылку) является надежным методом определения обнуляемости.
PS - про "обнуляемость"
Я должен повторить заявление об обнуляемости, которое я сделал в отдельном посте, который относится непосредственно к правильному решению этой темы. То есть, я полагаю, что основное внимание здесь должно быть сосредоточено не на том, как проверить, является ли объект универсальным типом Nullable, а на том, можно ли присвоить значение null объекту его типа. Другими словами, я думаю, что мы должны определить, является ли тип объекта обнуляемым, а не обнуляемым. Разница заключается в семантике, а именно в практических причинах определения обнуляемости, которая обычно имеет значение.
В системе, использующей объекты с типами, которые могут быть неизвестны до времени выполнения (веб-службы, удаленные вызовы, базы данных, каналы и т. Д.), Общим требованием является определение того, можно ли присвоить объекту нулевое значение или объект может содержать ноль. Выполнение таких операций над ненулевыми типами, вероятно, приведет к ошибкам, обычно исключениям, которые очень дороги как с точки зрения производительности, так и требований к кодированию. Чтобы принять чрезвычайно предпочтительный подход к упреждающему избеганию таких проблем, необходимо определить, способен ли объект произвольного типа содержать нуль; то есть, является ли это вообще 'обнуляемым'.
В очень практичном и типичном смысле обнуляемость в терминах.NET вовсе не обязательно означает, что тип объекта является формой обнуляемости. Фактически, во многих случаях объекты имеют ссылочные типы, могут содержать нулевое значение и, следовательно, могут иметь значение NULL; ни один из них не имеет типа Nullable. Поэтому для практических целей в большинстве сценариев следует проводить тестирование для общей концепции обнуляемости, в отличие от концепции Nullable, зависящей от реализации. Поэтому мы не должны зацикливаться на том, чтобы сосредоточиться исключительно на типе.NET Nullable, а должны включить наше понимание его требований и поведения в процесс сосредоточения внимания на общей практической концепции обнуляемости.
Будьте осторожны, когда бокс обнуляемого типа (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))
Он становится истинным ссылочным типом, поэтому вы теряете тот факт, что он был обнуляемым.
Может быть, немного не по теме, но все же некоторая интересная информация. Я нахожу много людей, которые используют Nullable.GetUnderlyingType() != null
к идентичности, если тип обнуляется. Это, очевидно, работает, но Microsoft советует следующее type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(см. http://msdn.microsoft.com/en-us/library/ms366789.aspx).
Я посмотрел на это с точки зрения производительности. Заключение теста (один миллион попыток), приведенное ниже, заключается в том, что, когда тип является обнуляемым, опция Microsoft обеспечивает наилучшую производительность.
Nullable.GetUnderlyingType (): 1335 мс (в 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();
}
Я думаю, что те, которые используют тестирование 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<>));
Вот то, что я придумал, так как все остальное казалось неудачным - по крайней мере, в ПЛК - Portable Class Library / .NET Core с>= C# 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;
}
}
Используя отражение и type.IsGenericType
... не работал на моем текущем наборе.NET Runtime. Также не помогла документация MSDN.
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
Частично потому, что API-интерфейс Reflection значительно изменился в.NET Core.
Эта версия:
- кеширование результатов происходит быстрее,
- не требует ненужных переменных, таких как 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;
Простой способ сделать это:
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);
}