Как уже упоминалось, вы можете узнать, является ли универсальный тип типом класса или типом значения, вызвав IsValueType
для объекта типа.Однако обратите внимание, что не имеет смысла проверять, является ли объект экземпляр типом значения.Хотя C # будет делать вид, что поля ссылочного типа могут содержать экземпляр типа значения, это иллюзия.Для каждого типа значения есть соответствующий тип класса с тем же именем и полями.Вызов GetType().IsValueType
в хранилище, содержащем ссылку на такой экземпляр, выдаст значение true, даже если хранилище фактически содержит ссылку на объект класса.Это можно легко продемонстрировать с помощью структур, которые реализуют изменяющиеся интерфейсы (например, List<string>.Enumerator
, который я буду называть LSE
для краткости).
LSE
, который хранится в переменной типа LSE
будет вести себя как тип значения.Копирование LSE
, который собирается вернуть третий элемент списка, в другую переменную типа LSE
даст второй, независимый перечислитель, который также собирается вернуть третий элемент.Можно читать элементы из любого перечислителя, не влияя на другого.Напротив, если у вас есть переменная типа IEnumerable<string>
, которая содержит ссылку на LSE
, и одна копирует ее в другую переменную типа IEnumerable<string>
, у другой будет вторая ссылка на того же самого перечислитель.Чтение элементов из одного перечислителя также продвинет другого.Другими словами, копирование LSE
в хранилище ссылочного типа приведет к чему-то, что будет вести себя как ссылочный тип, потому что под крышками это будет фактически один.
Обратите внимание, что вызов GetType
в месте хранения не будет показано, содержит ли он тип реального значения или содержит ли он объект класса, связанный с типом значения.В первом случае система создаст новый экземпляр класса, связанный с типом значения, и вызовет GetType
для этого, поэтому в обоих случаях вызов GetType
будет фактически обработан в экземпляре типа класса.