Имеет ли смысл использовать "как" вместо броска, даже если нет нулевой проверки?
В блогах разработки, примерах онлайн-кода и (недавно) даже книге я постоянно спотыкаюсь о коде:
var y = x as T;
y.SomeMethod();
или, что еще хуже:
(x as T).SomeMethod();
Это не имеет смысла для меня. Если вы уверены, что x
имеет тип T
, вы должны использовать прямой листинг: (T)x
. Если вы не уверены, вы можете использовать as
, но перед тем, как выполнить некоторую операцию, вам нужно проверить null
. Все, что делает вышеприведенный код, это превратить (полезный) InvalidCastException
в (бесполезный) NullReferenceException
.
Я единственный, кто считает, что это вопиющее злоупотребление ключевыми словами as
? Или я пропустил что-то очевидное, и приведенная выше картина действительно имеет смысл?
Ответы
Ответ 1
Ваше понимание верно. Это похоже на попытку микро-оптимизации для меня. Вы должны использовать обычный бросок, когда вы уверены в типе. Помимо создания более разумного исключения, он также быстро выходит из строя. Если вы ошибаетесь в своем предположении о типе, ваша программа немедленно сработает, и вы сможете сразу увидеть причину сбоя, а не ждать NullReferenceException
или ArgumentNullException
или даже логической ошибки когда-нибудь в будущее. В общем случае выражение as
, которое не сопровождается проверкой null
где-то, является запахом кода.
С другой стороны, если вы не уверены в отливке и ожидаете, что он сработает, вы должны использовать as
вместо обычного литья, завернутого в блок try-catch
. Кроме того, рекомендуется использовать as
для проверки типа, за которой следует бросок. Вместо:
if (x is SomeType)
((SomeType)x).SomeMethod();
который генерирует isinst
инструкцию для ключевого слова is
и castclass
для актера (эффектно выполняющий бросок дважды), вы должны использовать:
var v = x as SomeType;
if (v != null)
v.SomeMethod();
Это генерирует команду isinst
. Первый метод имеет потенциальный недостаток в многопоточных приложениях, поскольку условие гонки может заставить переменную изменять свой тип после того, как проверка is
прошла успешно и завершилась неудачей в литой строке. Последний метод не подвержен этой ошибке.
Следующее решение не рекомендуется для использования в производственном коде. Если вы действительно ненавидите такую фундаментальную конструкцию в С#, вы можете подумать о переходе на VB или какой-либо другой язык.
В случае, если один отчаянно ненавидит синтаксис каста, он/она может написать метод расширения для имитации акта:
public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
return (T)o;
}
и используйте аккуратный синтаксис [?]:
obj.To<SomeType>().SomeMethod()
Ответ 2
IMHO, as
просто имеет смысл в сочетании с проверкой null
:
var y = x as T;
if (y != null)
y.SomeMethod();
Ответ 3
Использование 'as' не применяет определенные пользователем преобразования, в то время как приведение будет использовать их там, где это необходимо. Это может быть важным различием в некоторых случаях.
Ответ 4
Я немного написал об этом здесь:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Я понимаю вашу мысль. И я согласен с этим: оператор передачи сообщает: "Я уверен, что этот объект может быть преобразован в этот тип, и я готов рискнуть исключением, если я ошибаюсь", тогда как оператор "as" сообщает об этом "Я не уверен, что этот объект может быть преобразован в этот тип, дайте мне null, если я ошибаюсь".
Однако есть тонкая разница. (x как T). Независимо от того, что() сообщает "Я знаю не только, что x может быть преобразован в T, но, кроме того, это делает только ссылки или конверсии для распаковки, а кроме того, что x не является нулевым". Это передает различную информацию, чем ((T) x). В любом случае(), и, возможно, это то, что намеревается автор кода.
Ответ 5
Я часто видел ссылки на эту вводящую в заблуждение статью в качестве доказательства того, что "как" быстрее, чем кастинг.
Одним из наиболее очевидных вводящих в заблуждение аспектов этой статьи является графическое изображение, которое не указывает на то, что измеряется. Я подозреваю, что он измеряет неудачные касты (где "как" , очевидно, намного быстрее, поскольку не исключено исключение).
Если вы потратите время на проведение измерений, вы увидите, что кастинг будет, как и следовало ожидать, быстрее, чем "как" , когда преследование преуспевает.
Я подозреваю, что это может быть одной из причин использования "культа груза" как ключевого слова "как" , а не для литья.
Ответ 6
Прямой бросок требует пары круглых скобок больше, чем ключевое слово as
. Таким образом, даже в случае, когда вы на 100% уверены, что такое тип, он уменьшает видимый беспорядок.
Согласился на исключение. Но, по крайней мере, для меня, большинство использования as
сворачиваются, чтобы проверить на null
после этого, что я нахожу более приятным, чем устранение исключения.
Ответ 7
В 99% случаев, когда я использую "как", это когда я не уверен, какой тип фактического объекта
var x = obj as T;
if(x != null){
//x was type T!
}
и я не хочу улавливать явные исключения исключений и делать два раза, используя "is":
//I don't like this
if(obj is T){
var x = (T)obj;
}
Ответ 8
Это просто потому, что людям нравится, как выглядит, это очень читаемо.
Давайте посмотрим правде в глаза: оператор литья/преобразования на языках C-типа довольно ужасен, с точки зрения читаемости. Я бы хотел, чтобы это было лучше, если С# принял либо синтаксис Javascript:
object o = 1;
int i = int(o);
Или определите оператор to
, эквивалент каста as
:
object o = 1;
int i = o to int;
Ответ 9
Людям нравится as
столько, что он заставляет их чувствовать себя в безопасности от исключений... Как гарантия на коробке. Парень надевает настоящую гарантию на коробку, потому что он хочет, чтобы вы чувствовали себя тепло и вкусно внутри. Вы полагаете, что ночью вы кладете эту маленькую коробочку под подушку, Гарантированная фея может спуститься и оставить четверть, я прав Тед?
Возвращаясь к теме... при использовании прямого трансляции существует вероятность недействительного исключения исключения. Поэтому люди применяют as
как общее решение для всех своих потребностей в литье, потому что as
(сам по себе) никогда не будет генерировать исключение. Но самое забавное в том, что в примере, который вы давали (x as T).SomeMethod();
, вы торгуете недействительным исключение литья для исключения с нулевой ссылкой. Что скрывает реальную проблему, когда вы видите исключение.
Я вообще не использую as
слишком много. Я предпочитаю тест is
, потому что для меня он выглядит более читаемым и имеет больше смысла, чем попытка трансляции и проверка на нуль.
Ответ 10
Это должно быть одним из моих топ-писем.
Stroustrup D & E и/или некоторые сообщения в блоге, которые я не могу найти сейчас, обсуждают понятие оператора to
, в котором будет рассмотрен пункт, сделанный https://stackoverflow.com/users/73070/johannes-rossel (т.е. тот же синтаксис, что и as
, но с семантикой DirectCast
).
Причина, по которой это не была реализована, заключается в том, что приведение должно причинять боль и быть уродливым, чтобы вас не отталкивали от использования.
Жаль, что "умные" программисты (часто авторы книг (Juval Lowy IIRC)) обошли это, злоупотребляя as
таким образом (С++ не предлагает as
, вероятно, по этой причине).
Даже у VB больше согласованности в наличии единого синтаксиса, который заставляет вас выбирать TryCast
или DirectCast
и ваш ум!
Ответ 11
Я считаю, что ключевое слово as
можно рассматривать как более элегантную версию
dynamic_cast
из С++.
Ответ 12
Вероятно, он более популярен по техническим причинам, а просто потому, что его легче читать и интуитивно понятнее. (Не сказать, что это лучше, просто пытается ответить на вопрос)
Ответ 13
Одна из причин использования "как":
T t = obj as T;
//some other thread changes obj to another type...
if (t != null) action(t); //still works
Вместо (плохого кода):
if (obj is T)
{
//bang, some other thread changes obj to another type...
action((T)obj); //InvalidCastException
}