Какой смысл использовать «is», затем «as» вместо «as», за которым следует проверка на ноль в C #? - PullRequest
22 голосов
/ 22 августа 2011

При чтении кода C # я нашел довольно любопытный фрагмент:

if( whatever is IDisposable) {
  (whatever as IDisposable).Dispose();
}

Я бы предпочел, чтобы это было сделано так:

if( whatever is IDisposable) { //check
  ((IDisposable)whatever).Dispose(); //cast - won't fail
}

или как это:

IDisposable whateverDisposable = whatever as IDisposable;
if( whateverDisposable != null ) {
   whateverDisposable.Dispose();
}

Я имею в виду as похоже на приведение, но возвращает null при неудаче. Во втором фрагменте он не может потерпеть неудачу (так как раньше была проверка is).

Какой смысл писать код в первом фрагменте, а не во втором или третьем?

Ответы [ 8 ]

15 голосов
/ 22 августа 2011

Вы правы. Первый фрагмент не имеет смысла.

Из альтернатив я бы порекомендовал третий, поскольку он является поточно-ориентированным в том смысле, что между проверкой и вызовом метода объект не может быть заменен другим, который не реализует интерфейс. Рассмотрим следующий сценарий со вторым фрагментом:

if (whatever is IDisposable) { //check 
    // <-- here, some other thread changes the value of whatever
    ((IDisposable)whatever).Dispose(); // could fail
} 

Этого не может быть с третьим фрагментом.

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

3 голосов
/ 22 августа 2011

is, за которым следует as - бессмысленная конструкция, как вы подозреваете. В конечном счете, если вы не уверены, что это за тип или какие интерфейсы он реализует, есть две идиомы - один для ссылочных типов и один для типов значений.

При тестировании для ссылочных типов (или в штучной упаковке) последний предоставленный вами шаблон верен - as с последующим нулевым тестом. Это будет самый быстрый подход, так как во время выполнения потребуется выполнить только один тип теста.

При проверке типов значений в штучной упаковке второй шаблон, который вы предоставляете, верен - is, за которым следует приведение.

3 голосов
/ 22 августа 2011

Нет смысла использовать as после того, как is истинно, я подозреваю, что код, который вы прочитали, или книга показывает только, как объявить и использовать as и is. Я бы использовал его, как вы предложили во втором фрагменте через:

IDisposable whateverDisposable = whatever as IDisposable;
if( whateverDisposable != null) 
{
   whateverDisposable.Dispose();
}

Обратите также внимание, что в первой попытке "((IDisposable)whatever)" вы применяете объект, для исполнения которого требуется "время на сотворение", здесь вы применяете дважды в первый раз, используя as, а во второй - в явном приведении. так что лучший способ - это реализовать второй метод "as IDisposable, а затем проверить, если он null".

2 голосов
/ 22 августа 2011

Согласитесь с точкой, но если вам действительно, как это выглядит чисто, как насчет метода расширения, такого как:

1 голос
/ 22 августа 2011

Чтобы ответить на ваш актуальный вопрос: опыт и привычка.

До включения ключевого слова as в .Net 2.0 единственным способом безопасно определить, можно ли привести объект к определенному типу / интерфейсу, было ключевое слово is.

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

if(whatever is IDisposable)  //check
{
  ((IDisposable)whatever).Dispose(); //cast - won't fail
}

Затем мы получили ключевое слово as safe-cast.Я предполагаю, что большинство людей, когда они впервые начали использовать as, продолжали использовать знакомый шаблон, но заменили прямое приведение на безопасное приведение, и их шаблон выбора трансформировался в ваш пример 1. (Я знаю, что сделалэто ненадолго.)

if(whatever is IDisposable) 
{
  (whatever as IDisposable).Dispose();
}

В конце концов, многие или большинство либо осознали сами, либо получили инструкции от fxCop или CodeAnalysis, что «правильный» шаблон - ваш пример 3:

IDisposable whateverDisposable = whatever as IDisposable;
if(whateverDisposable != null ) 
{
    whateverDisposable.Dispose();
}

Конечно, есть некоторые, кто еще находится на стадии примера 1 и по какой-то причине еще не «развил» свой паттерн до вашего примера 3, или другие, которые все еще просто используют старый добрый проверенный временем паттерниспользуя is с последующим прямым приведением.

1 голос
/ 22 августа 2011

is, за которым следует as, не имеет смысла, кроме того, что он быстрее набирается (из-за нажатий клавиш, требуемых для скобок, и того, насколько полезен intellisense для as).Значение Is немного медленнее, чем в других ваших примерах, потому что среда выполнения должна выполнить две проверки типов.

CLR имеет некоторую хитрость для as, и поэтому должен быть быстрееis с последующим приведением (однако имейте в виду, что вы не можете использовать as с типами значений).

1 голос
/ 22 августа 2011

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

1 голос
/ 22 августа 2011

Вы правы, первый отправленный вами код не нужен.Либо используйте второе, если вы хотите, чтобы область применения приведенной переменной находилась внутри if, либо используйте третье, если это не имеет значения, поскольку оно выполняет наименьшее количество работы.

...