Имеет ли смысл использовать "как" вместо приведения, даже если нет нулевой проверки?
В блогах разработчиков, онлайн-примерах кода и (в последнее время) даже в книге я постоянно спотыкаюсь о коде:
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();
Использование "как" не применяет определенные пользователем преобразования, в то время как приведение будет использовать их там, где это необходимо. Это может быть важным отличием в некоторых случаях.
Я написал немного об этом здесь:
Я понимаю вашу точку зрения. И я согласен с этим: оператор приведения сообщает: "Я уверен, что этот объект может быть преобразован в этот тип, и я готов рискнуть исключением, если ошибаюсь", тогда как оператор "как" сообщает "Я не уверен, что этот объект можно преобразовать в этот тип; если я ошибаюсь, дайте мне ноль".
Однако есть небольшая разница. (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
}