Завершите/Расположите шаблон в C#

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

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

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

  • Когда вы регистрируетесь, ваш сервер сборки должен запускать только модульные тесты, и они должны выполняться за несколько секунд, а не минут или часов.
  • Интеграционные тесты должны проводиться в течение ночи или вручную при необходимости.
368
задан samis 15 August 2016 в 02:37
поделиться

10 ответов

Рекомендуемый шаблон IDisposable - здесь . При программировании класса, использующего IDisposable, обычно следует использовать два шаблона:

При реализации запечатанного класса, который не использует неуправляемые ресурсы, вы просто реализуете метод Dispose, как с обычными реализациями интерфейса:

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

При реализации незапечатанного класса class сделайте это так:

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

Обратите внимание, что я не объявил финализатор в B ; вам следует реализовать финализатор только в том случае, если у вас есть фактические неуправляемые ресурсы, которые нужно удалить. CLR работает с финализируемыми объектами иначе, чем с нефинализируемыми объектами, даже если вызывается SuppressFinalize .

Таким образом, вы не должны объявлять финализатор, если вам не нужно, но вы даете наследникам вашего класса ловушку для вызова вашего Dispose и реализации финализатора, если они напрямую используют неуправляемые ресурсы:

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

Если вы не используете неуправляемые ресурсы напрямую ( SafeHandle ] и друзья не учитываются, поскольку они объявляют свои собственные финализаторы), то не реализуйте финализатор, так как GC по-другому работает с финализируемыми классами, даже если вы позже подавите финализатор. Также обратите внимание, что, хотя B не имеет финализатора, он по-прежнему вызывает SuppressFinalize , чтобы правильно работать с любыми подклассами, которые реализуют финализатор.

Когда класс реализует финализатор. IDisposable, это означает, что где-то есть некоторые неуправляемые ресурсы, от которых следует избавиться, когда вы закончите использовать класс. Фактические ресурсы инкапсулируются внутри классов; вам не нужно явно удалять их. Просто вызовите Dispose () или оберните класс в с помощью (...) {} , чтобы при необходимости избавиться от всех неуправляемых ресурсов.

413
ответ дан 23 November 2019 в 00:06
поделиться

1) WebClient - это управляемый тип, поэтому вам не нужен финализатор. Финализатор необходим в том случае, если ваши пользователи не используют Dispose () вашего класса NoGateway, а собственный тип (который не собирается сборщиком мусора) необходимо очистить после. В этом случае, если пользователь не вызывает Dispose (), содержащийся в нем WebClient будет удален сборщиком мусора сразу после того, как это сделает NoGateway.

2) Косвенно да, но вам не следует об этом беспокоиться. Ваш код верен как таковой, и вы не можете помешать своим пользователям очень легко забыть Dispose ().

2
ответ дан 23 November 2019 в 00:06
поделиться
  1. Если вы используете другие управляемые объекты, которые используют неуправляемые ресурсы, вы не несете ответственности за их завершение. Ваша ответственность - вызвать Dispose для этих объектов, когда Dispose вызывается для вашего объекта, и на этом он останавливается.

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

    • Знаете, что скоро у вас будут дефицитные ресурсы в ваших объектах, но не сейчас (и я имею в виду, что, как в «мы все еще разрабатываем, это будет здесь, прежде чем мы закончим» ", а не" Я думаю, нам это понадобится ")
    • Использование ограниченных ресурсов
  3. Да, код, использующий ваш код, должен вызывать метод Dispose вашего объекта. И да, код, использующий ваш объект, может использовать , используя , как вы показали.

  4. (снова 2?) Вероятно, что WebClient использует либо неуправляемые ресурсы, либо другие управляемые ресурсы, реализующие IDisposable. Однако точная причина не имеет значения. Важно то, что он реализует IDisposable, и поэтому вы должны действовать в соответствии с этими знаниями, удаляя объект, когда вы закончите с ним, даже если окажется, что WebClient вообще не использует никаких других ресурсов.

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

Насколько я знаю, настоятельно рекомендуется НЕ использовать Финализатор / деструктор:

public ~MyClass() {
  //dont use this
}

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

использование хорошо. используйте это :)

-4
ответ дан 23 November 2019 в 00:06
поделиться
using(NoGateway objNoGateway = new NoGateway())

эквивалентно

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

Финализатор вызывается после уничтожения вашего объекта сборщиком мусора. Это может быть совершенно в другое время, чем когда вы оставите свой метод. Dispose of IDisposable вызывается сразу после выхода из блока using. Следовательно, шаблон обычно используется для освобождения ресурсов сразу после того, как они вам больше не нужны.

2
ответ дан 23 November 2019 в 00:06
поделиться

Я согласен с PM100 (и должен был четко сказать это в моем предыдущем посте).

Вы никогда не должны реализовать IDSPosable в классе, если вам это не нужно. Чтобы быть очень конкретным, есть примерно в 5 раз, когда вам когда-либо понадобится / должен реализовать iDisposable:

  1. ваш класс явно содержит (то есть не через наследование) любые управляемые ресурсы, которые реализуются iDisposable и должны быть очищены после того, как ваш класс больше не будет использовал. Например, если ваш класс содержит экземпляр потока, DBCommand, DataTable и т. Д.

  2. Ваш класс явно содержит любые управляемые ресурсы, которые реализуют метод CLACE () - E.G. Idatareader, IDBConnection и т. Д. Обратите внимание, что некоторые из этих классов реализуются IDSPosable, имея распоряжение (), а также метод CLACK ().

  3. Ваш класс явно содержит неуправляемый ресурс - E.G. Объект COM, указатели (да, вы можете использовать указатели в управляемом C #, но они должны быть объявлены в «небезопасных» блоках и т. Д. В случае неуправляемых ресурсов вы также должны позаботиться о System.runtime.interopservices.marshal.releaseComobject () на RCW. Несмотря на то, что RCW, в теории, управляемая обертка, есть еще ссылка, подсчитываясь под крышками.

  4. Если ваш класс подписывается на события, используя сильные ссылки. Вам нужно отменить / отсоединить себя от событий. Всегда, чтобы убедиться, что они не ноль, прежде чем пытаться отменить / отсоединить их!

  5. Ваш класс содержит любую комбинацию вышеуказанного ...

Рекомендуемая альтернатива для работы с объектами COM и использование Marshal.releaseComobject () - использовать System.runtime.interopservices.Safehandle Class.

BCL (Base Class Library Team) имеет хорошее сообщение в блоге об этом здесь http://blogs.msdn.com/bclteam/chive/2005/03/16/396900.aspx

Важная заметка, чтобы сделать то, что если вы работаете с WCF и очистки ресурсов, вам почти всегда следует избегать блока «Использование». Там есть много сообщений блога и некоторые на MSDN о том, почему это плохое представление. Я также разместил об этом здесь - Не используйте «Использование ()» с прокси WCF

23
ответ дан 23 November 2019 в 00:06
поделиться

Никто не ответил на вопрос о том, должны ли вы реализовать IDSPosable, даже если вам это не нужно.

Краткий ответ: NO

Долгий ответ:

Это позволит потребителю вашего класса использовать «Использование». Вопрос, который я бы спросил, это - почему они это сделают? Большинство Devs не будет использовать «Использовать», если они не знают, что они должны - и как они знают. Либо

  • его обвигают их из опыта (например, класс сокетов)
  • его документированные
  • Они осторожны и могут видеть, что класс реализуется IDSPosable

, поэтому путем реализации IDSposable вы говорите Devs (по крайней мере, Некоторые) что этот класс заводит что-то, что должно быть освобождено. Они будут использовать «использование» - но есть и другие случаи, где использование невозможно (объем объекта не является локальным); И им придется начать беспокоиться о жизни объектов в тех других случаях - я бы наверняка волнулся. Но это не нужно

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

Так что не делай этого

10
ответ дан 23 November 2019 в 00:06
поделиться

Обратите внимание, что любая одноразовая реализация должна соответствовать приведенному ниже шаблону (IMHO). Я разработал этот шаблон на основе информации от нескольких отличных .NET "богов" из .NET Framework Design Guidelines (обратите внимание, что MSDN по каким-то причинам не следует этому шаблону!). Руководство по проектированию .NET Framework Design Guidelines было написано Кшиштофом Квалиной (тогдашний CLR Architect) и Брэдом Абрамсом (тогдашний CLR Program Manager) и Биллом Вагнером ([Effective C#] и [More Effective C#]) (просто взгляните на них на Amazon.com:

Обратите внимание, что вы НИКОГДА не должны реализовывать Финализатор, если только ваш класс не содержит (не наследует) ресурсы, управляемые ООН. Как только вы реализуете Финализатор в классе, даже если он никогда не вызывался, он гарантированно будет жить для дополнительной коллекции. Он автоматически помещается в Очередь финализации (которая работает в одном потоке). Кроме того, одно очень важное замечание... весь код, выполняемый в Финализаторе (если он вам нужен), ДОЛЖЕН быть потокобезопасным и безопасным для исключений! Плохие вещи будут происходить иначе... (т.е. неопределенное поведение, а в случае исключения - фатальное неустранимое падение приложения).

Паттерн, который я собрал (и написал фрагмент кода для), выглядит следующим образом:

#region IDisposable implementation

//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable

// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }

/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
public void Dispose()
{
    Dispose( true );

    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue 
    // and prevent finalization code for this object
    // from executing a second time.

    // Always use SuppressFinalize() in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize( this );
}

/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> equals false, the method has been called by the 
/// runtime from inside the finalizer and you should not reference 
/// other objects. Only unmanaged resources can be disposed.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
    // TODO If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    try
    {
        if( !this.IsDisposed )
        {
            if( isDisposing )
            {
                // TODO Release all managed resources here

                $end$
            }

            // TODO Release all unmanaged resources here



            // TODO explicitly set root references to null to expressly tell the GarbageCollector
            // that the resources have been disposed of and its ok to release the memory allocated for them.


        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );

        this.IsDisposed = true;
    }
}

//TODO Uncomment this code if this class will contain members which are UNmanaged
// 
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
//  ~$className$()
//  {
//     Dispose( false );
//  }
#endregion IDisposable implementation

Вот код для реализации IDisposable в производном классе. Обратите внимание, что нет необходимости явно перечислять наследование от IDisposable в определении производного класса.

public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)


protected override void Dispose( bool isDisposing )
{
    try
    {
        if ( !this.IsDisposed )
        {
            if ( isDisposing )
            {
                // Release all managed resources here

            }
        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );
    }
}

Я разместил эту реализацию в своем блоге: Как правильно реализовать диспозиционную схему

37
ответ дан 23 November 2019 в 00:06
поделиться

Официальный шаблон для реализации IDSposable трудно понять. Я верю, что это лучше лучше :

public class BetterDisposableClass : IDisposable {

  public void Dispose() {
    CleanUpManagedResources();
    CleanUpNativeResources();
    GC.SuppressFinalize(this);
  }

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

еще лучше решение - иметь правило, что вы всегда должны создать класс обертки для любого неуправляемого ресурса Что вам нужно обрабатывать:

public class NativeDisposable : IDisposable {

  public void Dispose() {
    CleanUpNativeResource();
    GC.SuppressFinalize(this);
  }

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

}

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

Результат для одноразовых классов, которые не имеют права непосредственно с неуправляемыми ресурсами, даже в присутствии наследования, является мощным: Они больше не должны беспокоиться с неуправляемыми ресурсами . Они будут простой для реализации и понимать:

public class ManagedDisposable : IDisposable {

  public virtual void Dispose() {
    // dispose of managed resources
  }

}
123
ответ дан 23 November 2019 в 00:06
поделиться

Шаблон из msdn

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}
2
ответ дан 23 November 2019 в 00:06
поделиться
Другие вопросы по тегам:

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