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

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

var y = x as T;
y.SomeMethod();

или, что еще хуже:

(x as T).SomeMethod();

Это не имеет смысла для меня. Если вы уверены, что x имеет тип T, вы должны использовать прямое приведение: (T)x, Если вы не уверены, вы можете использовать as но нужно проверить null перед выполнением какой-либо операции. Все, что делает приведенный выше код, это включить (полезно) InvalidCastException в (бесполезно) NullReferenceException,

Я единственный, кто считает, что это вопиющее злоупотребление as ключевое слово? Или я упустил что-то очевидное, и приведенная выше схема действительно имеет смысл?

13 ответов

Решение

Ваше понимание верно. Это звучит как попытка микрооптимизации для меня. Вы должны использовать обычное приведение, когда вы уверены в типе. Помимо генерации более разумного исключения, оно также быстро терпит неудачу. Если вы ошибаетесь в своем предположении о типе, ваша программа сразу же потерпит неудачу, и вы сможете сразу увидеть причину ошибки, а не ждать 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 проверка прошла успешно и провалилась на линии приведения. Последний метод не подвержен этой ошибке.


Следующее решение не рекомендуется для использования в производственном коде. Если вы действительно ненавидите такую ​​фундаментальную конструкцию в C#, вы можете подумать о переходе на VB или другой язык.

В случае, если кто-то отчаянно ненавидит синтаксис приведения, он может написать метод расширения для имитации приведения:

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

и используйте аккуратный синтаксис [?]:

obj.To<SomeType>().SomeMethod()

ПО МОЕМУ МНЕНИЮ, as просто имеет смысл в сочетании с null проверять:

var y = x as T;
if (y != null)
    y.SomeMethod();

Использование "как" не применяет определенные пользователем преобразования, в то время как приведение будет использовать их там, где это необходимо. Это может быть важным отличием в некоторых случаях.

Я написал немного об этом здесь:

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

Я понимаю вашу точку зрения. И я согласен с этим: оператор приведения сообщает: "Я уверен, что этот объект может быть преобразован в этот тип, и я готов рискнуть исключением, если ошибаюсь", тогда как оператор "как" сообщает "Я не уверен, что этот объект можно преобразовать в этот тип; если я ошибаюсь, дайте мне ноль".

Однако есть небольшая разница. (x как T). Что бы ни () сообщало: "Я знаю не только то, что x может быть преобразовано в T, но, кроме того, это включает в себя только ссылочные или распаковывающие преобразования, и, кроме того, что x не является нулевым". Это действительно передает информацию, отличную от ((T)x).Wh независимо (), и, возможно, именно это намеревается автор кода.

Я часто видел ссылки на эту вводящую в заблуждение статью как свидетельство того, что "как" быстрее, чем приведение.

Одним из наиболее очевидных вводящих в заблуждение аспектов этой статьи является график, который не указывает, что измеряется: я подозреваю, что он измеряет неудачные приведения (где "как", очевидно, намного быстрее, поскольку не выбрасывается исключение).

Если вы потратите время на измерения, то увидите, что приведение происходит, как и следовало ожидать, быстрее, чем "как", когда приведение завершается успешно.

Я подозреваю, что это может быть одной из причин "культа груза" использования ключевого слова as вместо приведения.

Прямое приведение требует пары скобок больше, чем as ключевое слово. Так что даже в том случае, если вы на 100 % уверены, что это за тип, это уменьшает визуальный беспорядок.

Договорились об исключении, хотя. Но по крайней мере для меня, большинство применений as сварить, чтобы проверить null впоследствии, что я считаю лучше, чем ловить исключение.

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; 
}

Это просто потому, что людям нравится, как это выглядит, это очень читабельно.

Посмотрим правде в глаза: оператор приведения / преобразования в C-подобных языках довольно ужасен с точки зрения читаемости. Мне бы лучше, если бы C# принял любой синтаксис Javascript:

object o = 1;
int i = int(o);

Или определить to оператор, эквивалент литья as:

object o = 1;
int i = o to int;

Людям нравитсяas так много, потому что это заставляет их чувствовать себя в безопасности от исключений... Как гарантия на коробке. Парень ставит на коробку причудливую гарантию, потому что хочет, чтобы вы чувствовали внутри себя тепло и жарко. Вы полагаете, что кладете эту маленькую коробочку под подушку ночью, Фея Гарантии может спуститься и покинуть четверть, я прав, Тед?

Вернемся к теме... при использовании прямого приведения существует вероятность недопустимого исключения приведения. Так люди применяют as как общее решение всех их потребностей в кастинге, потому что as (само по себе) никогда не вызовет исключения. Но самое забавное в том, что вы привели пример (x as T).SomeMethod(); вы обмениваете недопустимое исключение приведения на исключение с нулевой ссылкой. Что скрывает реальную проблему, когда вы видите исключение.

Я вообще не пользуюсь as перебор. Я предпочитаю is test, потому что для меня это выглядит более читабельным и имеет больше смысла, чем попытка приведения и проверка на ноль.

Это должно быть одним из моих главных раздражителей.

D&E Страуструпа и / или какой-то пост в блоге, который я не могу найти прямо сейчас, обсуждает понятие to оператор, который будет учитывать точку зрения Joey (т. е. тот же синтаксис, что и as но с DirectCast семантика).

Причина, по которой это не было реализовано, заключается в том, что приведение должно причинять боль и быть уродливым, поэтому вы отталкиваетесь от его использования.

Жаль, что "умные" программисты (часто авторы книг (Ювал Лоуи IIRC)) обходят это, злоупотребляя as таким образом (C++ не предлагает asнаверное по этой причине).

Даже VB обладает большей последовательностью, имея единый синтаксис, который заставляет вас выбирать TryCast или же DirectCast и решайся!

Я считаю, что as Ключевое слово можно рассматривать как более элегантную версию dynamic_cast из C++.

Вероятно, он более популярен не по техническим причинам, а только потому, что его легче читать и он более интуитивно понятен. (Не сказать, что это лучше, просто пытаясь ответить на вопрос)

Одна из причин использования "как":

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
}
Другие вопросы по тегам