Подтвердить что ты не робот

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

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

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).

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Это не может произойти с третьим фрагментом.

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

public static void DisposeIfNecessary(this object obj)
{
   if (obj != null && obj is IDisposable)
      ((IDisposable)obj).Dispose();
}

Ответ 6

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

Ответ 7

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

Ответ 8

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

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