IDisposable: действительно ли необходимо проверить на пустой указатель на наконец {}?

В большинстве примеров, которые Вы находите в сети, явно не используя "использование", шаблон смотрит что-то как:

  SqlConnection c = new SqlConnection(@"...");
  try {
    c.Open();
  ...
  } finally {
    if (c != null) //<== check for null
      c.Dispose();
  }

Если Вы действительно используете "использование" и смотрите на сгенерированный код IL, Вы видите, что это генерирует чек для пустого указателя

L_0024: ldloc.1 
L_0025: ldnull 
L_0026: ceq 
L_0028: stloc.s CS$4$0000
L_002a: ldloc.s CS$4$0000
L_002c: brtrue.s L_0035
L_002e: ldloc.1 
L_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0034: nop 
L_0035: endfinally 

Я понимаю, почему IL переводится для проверки на пустой указатель (не знает то, что Вы сделали в блоке использования), но если Вы используете попытку.. наконец и Вы имеете полный контроль над тем, как объект IDisposable привыкает в попытке.. наконец блок, необходимо ли действительно проверить на пустой указатель? если так, почему?

11
задан BlackTigerX 8 April 2010 в 05:53
поделиться

4 ответа

операторы using могут инициализировать переменные вызовами, отличными от конструкторов. Например:

using (Foo f = GetFoo())
{
    ...
}

Здесь f легко может быть нулевым, тогда как вызов конструктора никогда не может 1 вернуть null. Вот почему оператор using проверяет значение null. Это не имеет отношения к тому, что находится внутри самого блока, потому что оператор using сохраняет исходное начальное значение. Если вы напишете:

Stream s;
using (s = File.OpenRead("foo.txt"))
{
    s = null;
}

, поток все равно будет удален. (Если переменная объявлена ​​ в части инициализатора оператора using , она все равно доступна только для чтения.)

В вашем случае, как вы знаете, c не является нулевым до того, как вы войдете в блок try , вам не нужно проверять нулевое значение в блоке finally , если вы не переназначаете его значение (что я искренне надеюсь вы не!) внутри блока.

Теперь с вашим текущим кодом есть небольшой риск того, что может возникнуть асинхронное исключение между назначением c и входом в блок try - но трудно полностью избежать такого рода состояния гонки, поскольку асинхронное исключение может также возникнуть после завершения работы конструктора, но до того, как значение будет вообще присвоено c . Я бы посоветовал большинству разработчиков не беспокоиться о подобных вещах - асинхронные исключения имеют тенденцию быть достаточно «сложными», чтобы они в любом случае остановили процесс.

Есть ли причина, по которой вы все равно не хотите просто использовать оператор using? Честно говоря, я очень редко пишу свои собственные блоки finally в наши дни ...


1 См. Ответ Марка и плачу. Хотя обычно это не актуально.

12
ответ дан 3 December 2019 в 06:20
поделиться

Джон уже затронул здесь основную мысль ... но это всего лишь случайная мелочь; ваш конструктор может вернуть null . Не совсем частый случай и, конечно, не настоящая причина того, что с использованием делает это, и вы действительно не должны мучить свой код для этого - но это может случиться (ищите MyFunnyProxy ).

2
ответ дан 3 December 2019 в 06:20
поделиться

В вашем примере проверка на null не требуется, поскольку c не может иметь значение null после явного построения.

В следующем примере (аналогично тому, что генерируется оператором using), конечно же, необходима проверка на null:

SqlConnection c = null; 
  try { 
    c = new SqlConnection(@"..."); 
    c.Open(); 
  ... 
  } finally { 
    if (c != null) //<== check for null 
      c.Dispose(); 
  } 

Кроме того, проверка на null потребуется в следующем случае, как вы можете Не нужно быть уверенным, что CreateConnection не вернет null:

SqlConnection c = CreateConnection(...); 
  try { 
    c.Open(); 
  ... 
  } finally { 
    if (c != null) //<== check for null 
      c.Dispose(); 
  } 
5
ответ дан 3 December 2019 в 06:20
поделиться

Интересно ... Я использую VS2010 и нахожу это с помощью собственных фрагментов кода (R :) - с использованием блоков рок.

  • В блоке using вы не можете присвоить значение null (или что-то еще) переменной блока. Это приводит к ошибке компилятора . CS1656
  • Затем назначается переменная блока с помощью фабричного метода, который возвращает значение null. В этом случае пустой блок using не вызывает Dispose. (Очевидно, вы получите исключение NullReferenceException, если попытаетесь использовать блок var)
  • Затем в блоке try, правил нет, вы можете присвоить переменной значение null. Следовательно, проверка нуля перед Dispose является обязательной в блоке finally.
0
ответ дан 3 December 2019 в 06:20
поделиться
Другие вопросы по тегам:

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