Гарантируется ли совпадение двух выражений?
В универсальном методе Foo<T> where T : new() ... всегда ли это выражение true?
typeof(T) == new T().GetType()
Я не понимаю, как это может быть ложным, но, возможно, я упускаю крайний случай.
Я не думаю, что это рассматривается в любом из следующих связанных вопросов:
Немного тангенциально, но typeof(T) не заботятся о параметрах конструктора T are. new T().GetType() делает: для этого требуется конструктор без параметров.





Я полагаю, что они всегда дают один и тот же результат. Разница в том, что для typeof вам не нужно создавать новый экземпляр T, что скорее хорошо.
Также семантически более очевидно, если вы используете typeof для проверки типа, а не для создания экземпляра объекта.
typeof(T) и new T().GetType() не совсем идентичны. На самом деле new T().GetType() не «безопасен».
Учтите, что когда T является int? или чем-то подобным, тогда new T() приведет к null — фактически экземпляру int? с HasValue = false, который на самом деле является структурой. Упаковывание типа значения, допускающего значение NULL, который не имеет значения, приводит к null, а вызов метода, унаследованного от object, сначала упаковывает значение. Таким образом, в этом случае new T().GetType() выдает исключение нулевой ссылки.
всегда ли верно это выражение
typeof(T) == new T().GetType()
Возможно, оно никогда не бывает ложным (т. е. либо истинным, либо не оценивается как значение), но я могу упустить какой-то другой странный пограничный случай.
Я думаю, что конструктор для T тоже может генерировать исключение, но я не думаю, что typeof(T) может генерировать.
Это зависит от того, что вы имеете в виду. Если вы создаете экземпляр T (ваш var x = new T();), а затем сразу после написания
Type t1 = x.GetType();
и
if (t1 == typeof(T)) {... } // probably always true by definition
но что, если ваш T, скажем, не абстрактный класс и имеет подкласс R? Ну, это меняет картину, не так ли? Я имею в виду, что GetType() — это тип времени выполнения (а не статический тип компиляции, такой как typeof(T)). Итак (псевдокод здесь ниже)
var x = new T();
var t1 = x.GetType(); // T
...
x = new R();
...
bool chk1 = t1 == typeof(T); // true;
t1 = x.GetType(); // R
bool chk2 = t1 == typeof(T); // false
Мне непонятно, почему вы выдвинули гипотезу о существенно другом наборе кода, когда я дал конкретное (действительное) выражение, о котором я спрашивал?
В универсальном методе
Foo<T> where T : new()... всегда ли верно это выражение?
typeof(T) == new T().GetType()
К сожалению нет. Для ссылочных типов (включая ссылочные типы, допускающие значение NULL) с конструкторами по умолчанию — да, но это не относится к типам значений, допускающим значение NULL.
Где T — это int? или DateTime? или любой другой тип значения, допускающий значение NULL, результат new T() равен нулю. Вызов GetType() на null, конечно же, приведет к NullReferenceException. Но попробуем другое:
static Type? WhichType<T>(T value) => value?.GetType();
Это не вызовет исключение, но может вернуть null. Теперь давайте попробуем:
var type1 = WhichType<int?>(0);
var type2 = WhichType((int?)0);
Поскольку типы значений, допускающие значение NULL, обрабатываются компилятором C# особым образом, не совсем ясно, будет ли результат вызова GetType() для типа значения, допускающего значение NULL, возвращать внутренний тип, и это имеет некоторый смысл. К сожалению, это не очень поможет вам, если кто-то вызовет ваш общий метод с неправильным типом.
И если вам интересно, это работает с любым типом значения, а не только с примитивами значений:
public struct TestType { }
var type1 = WhichType<DateTime?>(DateTime.Now);
var type2 = WhichType<TestType?>(new TestType());
Это может быть проблемой, когда вы пытаетесь сравнить тип значения с ожидаемым типом, где T — это тип значения, допускающий значение NULL. В этом случае вам нужно использовать немного кода, чтобы разобраться.
var targetType = typeof(T);
if (targetType.IsConstructedGenericType &&
targetType.GetGenericTypeDefinition() == typeof(Nullable<>)
)
targetType = Nullable.GetUnderlyingType(targetType);
Это при необходимости развернет Nullable<> из внешнего типа, что позволит вам сравнить его с типом экземпляра объекта.
Этот код создает объекты конкретного объекта System.RuntimeType. Цитата: все классы, начинающиеся со слова Runtime, создаются только один раз для каждого объекта в системе и поддерживают операции сравнения