#c# #.net #performance #value-type
#c# #.net #Производительность #тип значения
Вопрос:
ПРЕДУПРЕЖДЕНИЕ: ЭТОТ КОД ОТСТОЙ, СМОТРИТЕ КОММЕНТАРИИ ЭНТОНИ
Что быстрее?
1.
public bool IsValueType<T>(T obj){
return obj is ValueType;
}
2.
public bool IsValueType<T>(T obj){
return obj == null ? false : obj.GetType().IsValueType;
}
3.
public bool IsValueType<T>(T obj){
return default(T) != null;
}
4. Что-то еще
Комментарии:
1. Действительно ли производительность имеет значение, потому что это действительно микрооптимизация
2. Методы 2 и 3 в том виде, в каком они написаны, недопустимы.
obj == null ||
вернет true для ссылочных типов.default(T) != null
вернет false дляNullable<T>
структур.3. Ваша правка в методе 2 по-прежнему будет недействительной.
obj != null ||
вернет true для объектов ссылочного типа, отличных от null.4. Поскольку я слишком критичен, э-э, я имею в виду полезен, метод 1 не любит обнуленные
Nullable<T>
объекты.int? bar = null;
Передайте это через функцию, вы получите false . (Честно говоря, не ожидал этого.)5. Метод 2 последняя правка.
return obj == null ? false : ...
все еще представляет проблему дляNullable<T>
.
Ответ №1:
На самом деле вы не тестируете объект — вы хотите протестировать тип. Чтобы вызвать их, вызывающий должен знать тип, но … meh. Учитывая подпись <T>(T obj)
, единственный разумный ответ:
public bool IsValueType<T>() {
return typeof(T).IsValueType;
}
или если мы хотим использовать пример объекта для целей вывода типа:
public bool IsValueType<T>(T obj) {
return typeof(T).IsValueType;
}
для этого не требуется боксирование ( GetType()
это боксирование), и с Nullable<T>
ним не возникает проблем. Более интересный случай — это когда вы передаете object
…
public bool IsValueType(object obj);
здесь у нас уже есть серьезные проблемы с null
, поскольку это может быть пустое Nullable<T>
(структура) или класс. Но разумной попыткой было бы:
public bool IsValueType(object obj) {
return obj != null amp;amp; obj.GetType().IsValueType;
}
но обратите внимание, что это неверно (и нефиксируемо) для пустых Nullable<T>
файлов. Здесь становится бессмысленным беспокоиться о упаковке, поскольку мы уже упакованы.
Комментарии:
1. Есть ли способ обойти
IsValueType
свойство? Я использую .NET DNX, который не поддерживает это свойство.2.
typeof(ValueType).IsAssignableFrom(t)
также не работает.3. @Shimmy — в коде операционной системы —
if (default(T) != null)
должен сработать.4. В DNX / .NET Core вы можете сделать это следующим образом
typeof(your_type).GetTypeInfo().IsValueType
.
Ответ №2:
Моим первым ответом было бы написать простой тест и выяснить это самостоятельно.
Моим вторым ответом (без какого-либо тестирования с моей стороны, конечно) был бы вариант 1. Это самая простая проверка. Второй метод включает в себя две отдельные проверки, в то время как третий предполагает создание экземпляра типа по умолчанию.
Вам также следует учитывать удобочитаемость. Фреймворк уже предоставляет вам возможность иметь в вашем коде следующее:
if(someObj is ValueType)
{
// Do some work
}
Зачем вообще беспокоиться о создании метода, который просто превратил бы приведенное выше утверждение в (при условии, что вы сделали свой метод статическим и позволили компилятору определить универсальный тип):
if(IsValueType(someObj))
{
// Do some work
}
Комментарии:
1. спасибо — и я не выступал за создание метода для его тестирования. Я просто написал это таким образом для ясности относительно того, о чем я спрашивал
2.
(someObj is ValueType)
кажется проблематичным для nulledNullable<T>
. Я не гуру IL, но я считаю, что здесь задействован бокс, и это не очень хорошо сочетается с этим сценарием.3. Откуда вы знаете,
is
это самая простая проверка? Вы можете проверить, например, реализует ли объект интерфейс с ним, что не так «просто». Не знаю, как с этим справляется компилятор, но посмотрите, как реализованы IsAssignableFrom и вызываемый им ImplementInterface . Вы знаете что-то еще, или вы просто думаете,is
что это быстрее, потому что это выглядит проще?
Ответ №3:
Определение структуры фактически определяет два типа: тип значения и тип класса, который является производным от System.ValueType
. Если сделан запрос на создание переменной, параметра, поля или массива (совместно именуемого ‘место хранения’) типа, производного от System.ValueType , система вместо этого создаст место хранения, в котором будут храниться поля объекта, а не ссылка на объект, в котором эти поля отображаются. С другой стороны, если сделан запрос на создание экземпляра типа, производного от System.ValueType , система создаст экземпляр объекта класса, который является производным от System.ValueType.
Это можно продемонстрировать, создав структуру, которая реализует IValue:
интерфейс IValue {int value {get; set;}}; struct ValueStruct : IValue { общедоступное значение int {get; set;}}; }
с помощью универсальной процедуры тестирования и кода для ее переноса:
тест статической пустоты<T>(T it) где T:IValue { T дублировать = это; it.value = 1; дубликат.значение = 10; Консоль.Строка записи (it.value.toString()); } статический тест void () { ValueStruct v1 = новый ValueStruct(); v1.значение = 9; IValue v2 = v1; Тест<ValueStruct>(v1); Тест<ValueStruct>(v1); Тест<IValue>(v1); Тест<IValue>(v1); Тест<IValue>(v2); Тест<IValue>(v2); }
Обратите внимание, что в любом случае вызов GetType для параметра, переданного в Test, приведет к получению ValueStruct , который сообщит о себе как о типе значения. Тем не менее, переданный элемент будет иметь «реальный» тип значения только при первых двух вызовах. При третьем и четвертом вызовах это действительно будет тип класса, о чем свидетельствует тот факт, что изменение на duplicate
повлияет it
. И при пятом и шестом вызовах изменение будет распространено обратно на версию v2, так что второй вызов «увидит» его.
Комментарии:
1. Обычно это можно описать как пакетирование… При третьем и четвертом вызовах вы помещаете в коробку сам вызов метода: когда T является интерфейсом,
it
это значение в штучной упаковке (иногда это поле можно оптимизировать), а duplicate — это просто ссылка на это поле. При пятом и шестом вызове вы передаете объекты, которые уже упакованы, потому чтоIValue v2 = v1;
создали коробку. Поскольку вы дважды вводите одно и то же поле вместо создания двух отдельных полей, изменения, внесенные в поле при первом вызове, видны при втором вызове.2. @AnorZaken: Термин «упаковка» действительно используется для описания процесса. У меня нет документации для . NET internals удобен, но он действительно описывает процесс в терминах наличия двух отдельных типов, и я думаю, что признание того, что коробочная структура является
Object
, в то время как распакованная структура таковой не является, более понятно, чем модель абстракции, используемая в C #. VB.NET добавляет в смесь некоторую собственную глупую логику. Если ссылка на тип интерфейса идентифицирует экземпляр типа значения в штучной упаковке, преобразование ссылки в typeObject
приведет к повторной упаковке экземпляра по какой-то причине, которую я не совсем понимаю.
Ответ №4:
static class Metadata<T>
{
static public readonly Type Type = typeof(T);
static public readonly bool IsValueType = Metadata<T>.Type.IsValueType;
}
//fast test if T is ValueType
if(Metadata<T>.IsValueType) //only read static readonly field!
{
//...
}
Комментарии:
1. Ограничение этого заключается в том, что он основан на
typeof(T)
, а не на тестировании входящего экземпляра. Обычно программист знает, является ли конкретноеtype
значение или нет, общая потребность состоит в том, чтобы знать, является лиinstance
значение или нет. Рассмотрим параметр методаobject obj
. Этот ответ будет оценивать это на основеT=object
объявленного типа параметра, а не типа среды выполнения конкретного экземпляра, поэтому вернетfalse
, независимо от того, чтоobj
такое. Ноobj
это может быть целое число в штучной упаковке или другой тип значения.
Ответ №5:
Существует два правила:
1-Все классы являются ссылочными типами, такими как Object и String, поэтому они поддерживаются классами .NET Framework.
2.Все структуры являются типами значений, такими как bool и char, даже если они содержат ссылочный элемент, поэтому они поддерживаются структурами .NET Framework.
Просто щелкните правой кнопкой мыши на любом типе и перейдите к Определению, если это класс, это означает, что это ссылочный тип, иначе, если это структура, это означает, что это тип значения 🙂
Комментарии:
1. для справочной информации, но что вы подразумеваете под «даже если он содержит ссылочный элемент»? Кроме того, я предполагаю, что инструкции GUI относятся к Visual Studio, правильно?
2. Правильно, это ссылка на Visual Studio, и я имею в виду, что у вас может быть структура, содержащая ссылку на объект …. и вы могли бы найти эти 2 предложения на msdn.microsoft.com/en-us/library/t63sy5hs.aspx «класс — это ссылочный тип. По этой причине ссылочные типы, такие как Object и String, поддерживаются классами .NET Framework. Обратите внимание, что каждый массив является ссылочным типом, даже если его члены являются типами значений.» «структура — это тип значения, даже если она содержит элементы ссылочного типа. По этой причине такие типы значений, как Char и Integer, реализованы структурами .NET Framework.»
3. Интересно, но вопрос в коде, а не в том, чтобы выяснить это в IDE.
4. Вопрос был об универсальном типе T, помните? «Перейти к определению» не поможет с T.
Ответ №6:
Вы можете использовать
obj.GetType().IsValueType
Это использует отражение, но ясный способ вместо того, чтобы заботиться о распаковке упаковки.
Комментарии:
1. Этот ответ не отвечает на вопрос.