Имеет ли смысл использовать "как" вместо броска, даже если нет нулевой проверки?

В блогах разработки, примерах онлайн-кода и (недавно) даже книге я постоянно спотыкаюсь о коде:

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
}