Почему вообще приводятся ссылочные типы, когда вы можете использовать «как»? [duplicate]

6
задан Community 23 May 2017 в 12:27
поделиться

8 ответов

Рассмотрим следующие альтернативы:

Foo(someObj as SomeClass);

и:

Foo((SomeClass)someObj);

Из-за того, что someObj имеет неправильный тип, первая версия передает null в Foo ]. Некоторое время спустя это приводит к выбросу NullReferenceException . Сколько позже? Зависит от того, что делает Foo . Он может сохранить null в поле, а через несколько минут к нему обращается некоторый код, который ожидает, что он не будет null .

Но со второй версией проблема сразу же обнаруживается.

Почему усложняют исправление ошибок?

Обновление

OP спросил в комментарии: не проще ли использовать как , а затем проверить null в операторе if ?

Если null является неожиданным и свидетельствует об ошибке в вызывающей программе, вы можете сказать:

SomeClass c = someObj as SomeClass;
if (c == null)
{
    // hmm...
}

Что вы делаете в этом ] если -блок? Есть два общих решения. Один из них - создать исключение, поэтому ответственность за исправление ошибки лежит на вызывающем. В этом случае проще написать:

SomeClass c = (SomeClass)someObj;

Это просто избавляет вас от написания логики if / throw вручную.

Но есть и другая альтернатива. Если у вас есть «стандартная» реализация SomeClass , которую вы можете использовать там, где нет ничего лучшего (возможно, у него есть методы, которые ничего не делают или возвращают «пустые» значения и т. Д.), Тогда вы можете сделать this:

SomeClass c = (someObj as SomeClass) ?? _stockImpl;

Это гарантирует, что c никогда не будет нулевым . Но так ли уж лучше? Что делать, если у вызывающего абонента есть ошибка; разве ты не хочешь помогать в поиске ошибок? Заменяя объект по умолчанию, вы маскируете ошибку. Это звучит привлекательно, пока вы не потратите неделю своей жизни, пытаясь отследить ошибку.

(В некотором смысле это имитирует поведение Objective-C, в котором любая попытка использовать ссылку null никогда не приведет к сбору; она просто молча ничего не делает.)

9
ответ дан 8 December 2019 в 15:58
поделиться

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

2
ответ дан 8 December 2019 в 15:58
поделиться

Одна определенная причина заключается в том, что объект является или может быть (при написании универсального метода, вы можете не знать во время кодирования) приводим к типу значения, в котором case as не допускается.

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

if(obj is MyType)
  DoMyTypeStuff((MyType)obj);
else
  DoMoreGeneralStuff(obj);

Трудно оправдать использование в качестве здесь, поскольку единственное, что он действительно делает, - это добавляет избыточную проверку (возможно, она будет оптимизирована, а может, и нет). С другой стороны, если вы на полпути к состоянию транса с таким количеством информации, которое у вас есть в выгружаемой памяти вашего мозга, и на основании этого вы почти уверены, что объект должен быть типа вопрос, может лучше добавить в чек.

Еще одна веская причина в том, что разница между принадлежностью неправильного типа и нулевым скрывается как . Если разумно передавать строку в данный метод, включая нулевую строку, но нецелесообразно передавать int, то val as string только что заставило неправильное использование выглядеть как совершенно другое правильное использование, и вы только что сделали ошибку более трудной для поиска и потенциально более опасной.

Наконец, возможно, если вы не знаете тип объекта, вызывающий код должен это сделать. Если код вызова вызвал ваш неправильно, они должны получить исключение.Разрешить передачу InvalidCastException обратно или перехватить его и выбросить исключение InvalidArgument или подобное - это разумный и очевидный способ сделать это.

1
ответ дан 8 December 2019 в 15:58
поделиться

Если при написании кода для выполнения преобразования вы уверены, что приведение должно работать, следует использовать (DataGridView) MyObject . Таким образом, если приведение не удастся в будущем, ваше предположение о типе MyObject вызовет недопустимое исключение приведения в точке, где вы выполняете приведение, вместо исключения нулевой ссылки в какой-то момент позже.

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

tl; dr Если ваш код предполагает что-то, и это предположение неверно во время выполнения, код должен генерировать исключение.

1
ответ дан 8 December 2019 в 15:58
поделиться

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

Используя as, вы можете назначать типы, находящиеся ниже в дереве наследования, на более высокие. Например:

object o = "abc" as object;
DataGridView d = "abc" as DataGridView // doesn't do anything

DataGridView может создать настраиваемое приведение, которое позволяет это. Приведения определены для целевого типа и, следовательно, разрешают все, пока оно определено.

Еще одна проблема с as заключается в том, что это не всегда работает. Рассмотрим этот метод:

IEnumerable<T> GetList<T>(T item)
{
  (from ... select) as IEnumerable<T>
}

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

Конечно, вы всегда должны проверять наличие null при использовании ключевого слова as. Не предполагайте, что исключения не возникнут только потому, что ключевое слово их не выбрасывает. Не помещайте вокруг него Try {} Catch (NullReferenceException) {} , это не является ненужным и раздутым. Просто присвойте значение переменной и проверьте значение null, прежде чем использовать его. Никогда не используйте его встроенным в вызов метода.

0
ответ дан 8 December 2019 в 15:58
поделиться

Из MSDN ( as (ссылка C #) ):

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

0
ответ дан 8 December 2019 в 15:58
поделиться

оператор as работает только со ссылочными типами.

2
ответ дан 8 December 2019 в 15:58
поделиться

Принимая во внимание все комментарии, мы натолкнулись на это буквально на днях и задались вопросом, почему вы будете выполнять прямое приведение с использованием ключевого слова как . Что, если вы хотите, чтобы преобразование завершилось ошибкой? Иногда это желаемый эффект от приведения, если вы выполняете приведение из нулевого объекта. Затем вы помещаете исключение в стек вызовов.

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

0
ответ дан 8 December 2019 в 15:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: