используя рекомендации для дженериков и IDisposable - PullRequest
4 голосов
/ 02 ноября 2010

В пункте 5 «Более эффективного C #» представлено следующее:

public class EngineDriver<T> where T : IEngine, new()
{
  public void GetThingsDone()
  {
    T driver = new T();
    using (driver as IDisposable)
    {
      driver.DoWork();
    }
  }
}

Цель здесь - правильно утилизировать диск, если он реализует IDisposable. Это имеет смысл, но чем эта реализация отличается от более краткой:

public class EngineDriver<T> where T : IEngine, new()
{
  public void GetThingsDone()
  {
    using (T driver = new T())
    {
      driver.DoWork();
    }
  }
}

Разве код выше не должен вести себя точно так же? На самом деле, не является ли исходный код опасным в том смысле, что время жизни драйвера находится за пределами используемого блока, но драйвер удаляется в конце указанного блока?

Ответы [ 3 ]

10 голосов
/ 02 ноября 2010

Нет, поскольку T не обязательно реализует IDisposable (если только IEngine сам не реализует его) - в этом случае второй компилироваться не будет, а первый будет.

Относительно области действия драйвера -он все еще будет доступен после использования блока во втором примере, который не идеален, и попытка сделать это обычно приводит к исключению.В идеале вы должны иметь IEngine реализовать IDisposable или добавить дополнительное ограничение к EngineDriver, чтобы T реализовало его.

7 голосов
/ 02 ноября 2010

Очень важно, чтобы вы использовали доступные вам инструменты.Скомпилируйте предложенный код.Я подожду несколько минут.

Хорошо, ты вернулся.Да, вам нужно добавить ограничение для IDisposable, чтобы оператор using всегда мог избавиться от объекта.Код из книги - это взлом этого ограничения, он будет работать, даже если T не реализует IDisposable.использование (null) {} допустимо.

1 голос
/ 02 ноября 2010

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

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

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    {
      T driver = new T();
      using (driver as IDisposable) {
        driver.DoWork();
      }
    }
  }
}

Но для этого примера это перебор; сфера применения метода достаточна. Это имело бы смысл только в том случае, если ваш метод был бы больше, например:

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    {
      T driver = new T();
      using (driver as IDisposable) {
        driver.DoWork();
      }
    }
    // do more stuff, can't access driver here ....
  }
}

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

У вас также может быть еще один обзорный класс:

public class DisposableWrapper<T> : IDisposable {
  public T Item { get; private set; }
  public DisposableWrapper(T item) { Item = item; }
  public void Dispose() {
    using (Item as IDisposable) { }
    Item = default(T);
  }
}

public static class DisposableWrapperExtensions {
  public static DisposableWrapper<T> AsDisposable<T>(this T item) {
    return new DisposableWrapper<T>(item);
  }
}

public class EngineDriver<T> where T : IEngine, new() {
  public void GetThingsDone() {
    using (var driver = new T().AsDisposable()) {
      driver.Item.DoWork();
    }
  }
}

Это может иметь смысл, если вы используете много ссылок на интерфейсы, которые может реализовать IDisposable.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...