'использование' оператора по сравнению с 'попыткой наконец'

74
задан Peter Mortensen 29 July 2015 в 22:24
поделиться

13 ответов

Из MSDN Оператор использования (Ссылка C#)

оператор использования гарантирует, чтобы Расположили, назван, даже если исключение происходит, в то время как Вы - вызывающие методы для объекта. Можно достигнуть того же результата путем помещения объекта в блоке попытки, и затем вызов Располагают в наконец блок; на самом деле это - то, как оператор использования переводится компилятором. Пример кода ранее расширяется до следующего кода во время компиляции (отметьте дополнительные фигурные скобки для создания ограниченного объема для объекта):

{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

Так в основном, это - тот же код, но с хорошие автоматические пустые проверки и дополнительный объем для Вашей переменной . Документация также указывает, что "гарантирует корректное использование объекта IDisposable", таким образом, Вы могли бы также получать еще лучшую поддержку платформы для любых неясных случаев в будущем.

Так идут с опцией 2.

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

87
ответ дан chakrit 24 November 2019 в 12:00
поделиться

Я думаю, что метод 2 был бы лучше.

  • Более простой и больше читаемого кода в Ваших свойствах.
  • Менее подверженный ошибкам, так как код блокировки не должен несколько раз переписываться.
0
ответ дан Nathen Silver 24 November 2019 в 12:00
поделиться

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

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

Мы имеем дело со многими различными механизмами блокировки и поведениями. Ваш объект блокирования может иметь определенный тайм-аут, который использует. Ваша реализация блокировки может быть блокировкой монитора, блокировкой читателя, блокировкой писателя или спин-блокировкой. Однако с точки зрения вызывающей стороны все это не важно, о чем они заботятся, то, что контракт для блокировки ресурса соблюдают и что блокировка делает это способом, согласовывающимся с, он - реализация.

interface ILock {
    IDisposable Acquire();
}

class MonitorLock : ILock {
    IDisposable Acquire() { ... acquire the lock for real ... }
}

мне нравится Ваша модель, но я рассмотрел бы сокрытие механики блокировки от вызывающей стороны. FWIW, я измерил издержки метода использования по сравнению с попыткой наконец, и издержки выделения доступного объекта будут иметь между 2-3% производительности наверху.

0
ответ дан Ajaxx 24 November 2019 в 12:00
поделиться

DRY говорит: второе решение. Первое решение копирует логику использования блокировки, тогда как второе не делает.

4
ответ дан Peter Mortensen 24 November 2019 в 12:00
поделиться

Мне нравится 3-я опция

private object _myDateTimeLock = new object();
private DateTime _myDateTime;

public DateTime MyDateTime{
  get{
    lock(_myDateTimeLock){return _myDateTime;}
  }
  set{
    lock(_myDateTimeLock){_myDateTime = value;}
  }
}

Ваших двух опций, вторая опция является самой чистой и легче понять то, что продолжается.

4
ответ дан 24 November 2019 в 12:00
поделиться

"Набор свойств" и блокирующий в методе get свойства и уровне метода set смотрит неправильно. Ваша блокировка является слишком мелкомодульной. В самом типичном объектном использовании Вы хотели бы удостовериться, что Вы получили блокировку к доступу [еще 110] , чем одно свойство одновременно. Ваш конкретный случай мог бы отличаться, но я вид сомнения это.

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

4
ответ дан Hans Passant 24 November 2019 в 12:00
поделиться

Рассмотрите возможность, что оба решения плохи, потому что они маскируют исключения .

А try без catch должен, очевидно, быть плохой идеей; см. MSDN для того, почему using оператор аналогично опасен.

Примечание также Microsoft теперь рекомендует ReaderWriterLockSlim вместо ReaderWriterLock.

Наконец, обратите внимание, что примеры Microsoft используют два блока try-catch для предотвращения этих проблем, например,

try
{
    try
    {
         //Reader-writer lock stuff
    }
    finally
    {
         //Release lock
    }
 }
 catch(Exception ex)
 {
    //Do something with exception
 }

А простым, последовательным, чистым решением является хорошая цель, но принятие Вас не может только использовать lock(this){return mydateetc;}, Вы могли бы пересмотреть подход; с большим количеством информации я уверен Stack  Переполнение может помочь;-)

8
ответ дан Peter Mortensen 24 November 2019 в 12:00
поделиться

Я лично использую оператор "использования" C# максимально часто, но существует несколько определенных вещей, которые я делаю наряду с ним для предотвращения потенциальных упомянутых проблем. Проиллюстрировать:

void doSomething()
{
    using (CustomResource aResource = new CustomResource())
    {
        using (CustomThingy aThingy = new CustomThingy(aResource))
        {
            doSomething(aThingy);
        }
    }
}

void doSomething(CustomThingy theThingy)
{
    try
    {
        // play with theThingy, which might result in exceptions
    }
    catch (SomeException aException)
    {
        // resolve aException somehow
    }
}

Примечание, что я разделяю оператор "использования" на один метод и использование объекта (объектов) в другой метод с "попыткой" / блок "выгоды". Я могу вложить несколько операторов "использования" как это для связанных объектов (я иногда иду три или четыре глубоко в моем производственном коде).

В моем Dispose() методы для этих пользовательских IDisposable классы, я ловлю исключения (но НЕ ошибки) и регистрирую их (использующий Log4net). Я никогда не встречался с ситуацией, где любое из тех исключений могло возможно влиять на мою обработку. Потенциальным ошибкам, как обычно, позволяют распространить стек вызовов и обычно оконечную обработку с соответствующим сообщением (ошибка и отслеживание стека) зарегистрированный.

, Если я так или иначе встретился с ситуацией, где значительное исключение могло произойти во время Dispose(), я перепроектирую для той ситуации. Откровенно говоря, я сомневаюсь, что это будет когда-либо происходить.

Между тем, объем и преимущества очистки "использования" делают его одной из моих самых любимых функций C#. Между прочим, я работаю в Java, C# и Python как мои основные языки, с большим количеством других, добавленных тут и там, и "использование" является одной из моих самых любимых функций языка все вокруг, потому что это - практическая, повседневная рабочая лошадь.

5
ответ дан Rob Williams 24 November 2019 в 12:00
поделиться

Я определенно предпочитаю второй метод. Это более кратко при использовании и менее подвержено ошибкам.

В первом случае кто-то редактирующий код должен бояться вставлять что-либо между Получением (Read|Write) вызов Блокировки и попыткой.

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

12
ответ дан Rob Walker 24 November 2019 в 12:00
поделиться

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

Для блокировки чтения-записи попытка/выгода могла бы быть самой полезной, но Вы могли также использовать обоих, как так:

using (obj)
{
  try { }
  catch { }
}

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

1
ответ дан Luke 24 November 2019 в 12:00
поделиться

Глупый я. Есть способ сделать это еще проще, сделав заблокированные методы частью каждого экземпляра (вместо статических, как в моем предыдущем посте). Сейчас я действительно предпочитаю это, потому что нет необходимости передавать `rwlMyLock_m 'какому-либо другому классу или методу.

class StackOTest
{
    private delegate DateTime ReadLockMethod();
    private delegate void WriteLockMethod();

    static ReaderWriterLock rwlMyLock_m  = new ReaderWriterLock();
    private DateTime dtMyDateTime_m;
    public DateTime MyDateTime
    {
        get
        {
            return ReadLockedMethod(
                delegate () { return dtMyDateTime_m; }
            );
        }
        set
        {
            WriteLockedMethod(
                delegate () { dtMyDateTime_m = value; }
            );
        }
    }

    private DateTime ReadLockedMethod(ReadLockMethod method)
    {
        rwlMyLock_m.AcquireReaderLock(0);
        try
        {
            return method();
        }
        finally
        {
            rwlMyLock_m.ReleaseReaderLock();
        }
    }

    private void WriteLockedMethod(WriteLockMethod method)
    {
        rwlMyLock_m.AcquireWriterLock(0);
        try
        {
            method();
        }
        finally
        {
            rwlMyLock_m.ReleaseWriterLock();
        }
    }
}
-1
ответ дан 24 November 2019 в 12:00
поделиться

SoftwareJedi, у меня нет учетной записи, поэтому я не могу редактировать свои ответы.

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

class StackOTest
{
    static ReaderWriterLock rwlMyLock_m  = new ReaderWriterLock();
    private DateTime dtMyDateTime_m;
    public DateTime MyDateTime
    {
        get
        {
            DateTime retval = default(DateTime);
            ReadLockedMethod(
                delegate () { retval = dtMyDateTime_m; }
            );
            return retval;
        }
        set
        {
            WriteLockedMethod(
                delegate () { dtMyDateTime_m = value; }
            );
        }
    }

    private void ReadLockedMethod(Action method)
    {
        rwlMyLock_m.AcquireReaderLock(0);
        try
        {
            method();
        }
        finally
        {
            rwlMyLock_m.ReleaseReaderLock();
        }
    }

    private void WriteLockedMethod(Action method)
    {
        rwlMyLock_m.AcquireWriterLock(0);
        try
        {
            method();
        }
        finally
        {
            rwlMyLock_m.ReleaseWriterLock();
        }
    }
}
0
ответ дан 24 November 2019 в 12:00
поделиться

Я удивлен, что никто не предложил инкапсулировать try-finally в анонимные функции. Подобно технике создания экземпляров и удаления классов с помощью оператора using, блокировка сохраняется в одном месте. Я предпочитаю это сам только потому, что я предпочитаю читать слово «наконец», чем слово «удалить», когда я думаю о снятии блокировки.

class StackOTest
{
    private delegate DateTime ReadLockMethod();
    private delegate void WriteLockMethod();

    static ReaderWriterLock rwlMyLock_m  = new ReaderWriterLock();
    private DateTime dtMyDateTime_m;
    public DateTime MyDateTime
    {
        get
        {
            return ReadLockedMethod(
                rwlMyLock_m,
                delegate () { return dtMyDateTime_m; }
            );
        }
        set
        {
            WriteLockedMethod(
                rwlMyLock_m,
                delegate () { dtMyDateTime_m = value; }
            );
        }
    }

    private static DateTime ReadLockedMethod(
        ReaderWriterLock rwl,
        ReadLockMethod method
    )
    {
        rwl.AcquireReaderLock(0);
        try
        {
            return method();
        }
        finally
        {
            rwl.ReleaseReaderLock();
        }
    }

    private static void WriteLockedMethod(
        ReaderWriterLock rwl,
        WriteLockMethod method
    )
    {
        rwl.AcquireWriterLock(0);
        try
        {
            method();
        }
        finally
        {
            rwl.ReleaseWriterLock();
        }
    }
}
0
ответ дан 24 November 2019 в 12:00
поделиться
Другие вопросы по тегам:

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