Разница между decltype и typeof?
В c++ нет оператора typeof . Хотя это правда, что такая функциональность была предложена большинством компиляторов в течение некоторого времени, она всегда была расширением языка для конкретного компилятора. Поэтому сравнивать поведение двух в целом не имеет смысла, поскольку поведение typeof (если оно вообще существует) сильно зависит от платформы.
Поскольку теперь у нас есть стандартный способ получения типа переменной / выражения, на самом деле нет причин полагаться на непереносимые расширения, поэтому я бы сказал, он в значительной степени устарел .
Еще одна вещь, которую следует учитывать, заключается в том, что если поведение typeof несовместимо с decltype для данного компилятора, возможно, что расширение typeof не получит большого развития, чтобы охватить новые языковые функции в будущем (это означает, что он может просто не работать, например, с лямбдами). Я не знаю, так ли это в настоящее время, но это вполне возможно.
Что ж, typeof — нестандартное расширение GNU C, которое вы можете использовать в GNU C ++, потому что GCC позволяет вам использовать функции из других языков в другом (хотя и не всегда), поэтому сравнивать их не стоит.
Конечно, для других компиляторов могут существовать и другие нестандартные расширения, но GCC определенно является наиболее широко документированной реализацией.
Чтобы ответить на вопрос: он не может быть устаревшим, если никогда не был функцией.
Если вы хотите сравнить достоинства любого метода в C ++, семантически нет никакой разницы, если вы не имеете дело со ссылками. Вам следует использовать decltype , потому что он портативен и соответствует стандартам.
Typeof не был стандартизирован, хотя был реализован несколькими поставщиками компиляторов, например, GCC . Он стал устаревшим с decltype.
Хорошее объяснение недостатков typeof можно найти в соответствующем ответе .
Для устаревшего кода я успешно использовал следующее:
Разница между ними в том, что decltype всегда сохраняет ссылки как часть информации, а typeof — нет. Так.
Имя typeof было предпочтительным выбором (в соответствии с sizeof и alignof , и имя уже используется в расширениях), но, как вы можете видеть в предложении N1478, проблема совместимости с существующими реализации, отбрасывающие ссылки, привели к тому, что они дали ему отдельное имя.
«Мы используем имя оператора typeof, когда говорим о механизме запроса типа выражения в целом. Оператор decltype относится к предлагаемому варианту typeof. . Некоторые поставщики компиляторов (EDG, Metrowerks, GCC) предоставляют оператор typeof как расширение с семантикой отбрасывания ссылок. Как описано в разделе 4, это кажется идеальным для выражения типа переменных. С другой стороны, семантика отбрасывания ссылок не может обеспечить механизм для точного выражения возвращаемых типов универсальных functions . В этом предложении семантика оператора, который предоставляет информацию о типе выражений, отражает объявленный тип. Поэтому мы предлагаем назвать оператор decltype. «
Дж. Ярви, Б. Страуструп, Д. Грегор, Дж. Зик: Decltype и auto. N1478 / 03-0061.
Поэтому неправильно говорить, что decltype полностью исключил typeof (если вам нужна семантика отбрасывания ссылок, тогда расширение typeof в этих компиляторах все еще используется), но, скорее, typeof было в значительной степени устранено он плюс auto , который удаляет ссылки и заменяет использование, в котором typeof использовалось для вывода переменных.
Разница между nameof и typeof
Если вы хотите получить экземпляр класса в виде строки, невозможно сделать что-то подобное:
однако вы можете сделать что-то вроде:
В обоих случаях ( typeof и nameof ) возможен рефакторинг, поэтому я не вижу никакой другой причины изобретать другое ключевое слово более высокого уровня, например nameof , чтобы выполнить что-то это уже существует. Есть ли какие-то различия между ними, которые я не вижу четко?
Наконец, я был бы признателен, если бы кто-то указал мне на справочный источник, чтобы взглянуть на реализацию nameof . Использует ли он отражение?
Обновление 1 . Взято из здесь
nameof , очевидно, так же эффективен, как объявление строковой переменной. Никаких размышлений или чего бы то ни было!
Когда вы посмотрите на сгенерированный MSIL, вы увидите, что оно эквивалентно объявлению строки, потому что ссылка на строку объекта помещается в стек с помощью оператора ldstr:
7 ответов
nameof превращается в константу времени компиляции. typeof(. ).Name требует немного размышлений. Это не чрезмерно дорого, но в некоторых случаях это может повредить.
Во-вторых, он используется не для имен типов, а для других целей. Например, аргументы:
Вы также можете получить имена членов класса и даже местных жителей. Излишне говорить, что это очень полезно для отладки информации. Это также один из способов реализовать менее хрупкое отражение, например, когда парсинг деревьев выражений (к сожалению, в проекте, где я бы использовал это, мы все еще застряли на .NET 4.0 с C # 5 — это спасло бы меня от нескольких взломов тут и там).
И чтобы устранить некоторую путаницу, nameof не является , а не функцией, и typeof . Это оператор во время компиляции, и он всегда оценивается во время компиляции (хотя очевидно, что дженерики перемещают время компиляции немного дальше во времени).
Использование Reflection для генерации строк возможно, но не очень элегантно и не всегда возможно. Например, вы не можете использовать Reflection в изолированном коде. И вы не можете использовать его для локальных переменных. И это дорого.
Оператор nameof работает во время компиляции. Компилятор уже знает имя, когда он анализирует код. Так что можно тривиально сгенерировать строковый литерал. Очень быстро, не может быть быстрее и без ограничений времени выполнения.
Проверка типа: typeof, GetType или есть?
Лично я чувствую, что последний самый чистый, но есть ли что-то, что я пропускаю? Какой из них лучше использовать, или это личное предпочтение?
- typeof принимает имя типа (которое вы указываете во время компиляции).
- GetType получает тип времени выполнения экземпляра.
- is возвращает true, если экземпляр находится в дереве наследования.
пример
Как насчет typeof(T) ? Это также разрешается во время компиляции?
Да. T всегда является типом выражения. Помните, универсальный метод — это в основном целая куча методов с соответствующим типом. Пример:
использование typeof когда вы хотите получить тип во время компиляции . Используйте, GetType когда вы хотите получить тип во время выполнения . Редко какие-либо случаи можно использовать, is поскольку он выполняет приведение, и, в большинстве случаев, вы в любом случае заканчиваете приведение переменной.
Существует четвертый вариант, который вы не рассмотрели (особенно, если вы собираетесь привести объект к тому типу, который вы нашли); это использовать as .
Это только использует одно приведение, тогда как этот подход:
требует два .
Обновление (январь 2020 г.):
-
, теперь вы можете использовать inline, поэтому подход «is» теперь может быть реализован и в одном.
1.
Это незаконно, потому что typeof работает только с типами, а не с переменными. Я предполагаю, что obj1 является переменной. Таким образом, этот способ typeof статичен и выполняет свою работу во время компиляции, а не во время выполнения.
2.
Это true если obj1 точно типа int . Если obj1 происходит от int , условие if будет false .
3.
Это, true если obj1 is int , или если оно происходит от вызываемого класса int , или если он реализует вызываемый интерфейс int .
Это ошибка Оператор typeof в C # может принимать только имена типов, но не объекты.
Это будет работать, но, возможно, не так, как вы ожидаете. Для типов значений, как вы показали здесь, это приемлемо, но для ссылочных типов он будет возвращать true, только если тип был точно такого же типа, а не что-то еще в иерархии наследования. Например:
Это напечатало бы «o is something else» , потому что тип o — Dog нет Animal . Вы можете сделать эту работу, однако, если вы используете IsAssignableFrom метод Type класса.
Этот метод все еще оставляет большую проблему, хотя. Если ваша переменная равна нулю, вызов вызывает GetType() исключение NullReferenceException. Чтобы заставить его работать правильно, вы должны сделать:
При этом вы получаете эквивалентное поведение is ключевого слова. Следовательно, если вы хотите именно такое поведение, вам следует использовать is ключевое слово, которое более читабельно и более эффективно.
В большинстве случаев, однако, is ключевое слово все еще не то, что вы действительно хотите, потому что обычно недостаточно просто знать, что объект имеет определенный тип. Обычно вы хотите использовать этот объект как экземпляр этого типа, что также требует его приведения. И поэтому вы можете написать код вроде этого:
Но это заставляет CLR проверять тип объекта до двух раз. Он проверит его один раз, чтобы удовлетворить is оператора, и, если o это действительно так Animal , мы сделаем это снова, чтобы проверить приведение.
Лучше сделать это вместо этого:
as Оператор слепок , который не будет бросать исключение , если это не удается, вместо возвращения null . Таким образом, CLR проверяет тип объекта только один раз, и после этого нам просто нужно выполнить нулевую проверку, что более эффективно.
Но будьте осторожны: многие люди попадают в ловушку as . Поскольку он не генерирует исключения, некоторые люди думают о нем как о «безопасном» касте, и они используют его исключительно, избегая регулярных кастований. Это приводит к таким ошибкам:
В этом случае разработчик явно предполагает, что o это всегда будет Animal , и пока их предположение верно, все работает отлично. Но если они не правы, то то, что они в конечном итоге здесь, это NullReferenceException . С обычным броском они получили бы InvalidCastException вместо этого, который более правильно определил бы проблему.
Иногда эту ошибку бывает трудно найти:
Это еще один случай, когда разработчик явно ожидает, o что это будет Animal каждый раз, но это не очевидно в конструкторе, где используется as приведение. Это не очевидно, пока вы не дойдете до Interact метода, где animal ожидается , что поле будет назначено положительно. В этом случае вы не только получите ложное исключение, но оно не будет выдано до тех пор, пока потенциально не произойдет намного позже, чем произошла фактическая ошибка.
Если вам нужно только знать, относится ли объект к какому-либо типу, используйте is .
Если вам нужно обработать объект как экземпляр определенного типа, но вы точно не знаете, что объект будет этого типа, используйте as и проверьте null .
Если вам нужно обработать объект как экземпляр определенного типа, и объект должен быть этого типа, используйте обычное приведение.