2016-10-10 11 views
0

Мне нужно написать класс, где я хочу, чтобы потребитель мог утилизировать код, обернув код оператором using(...) в C#.Зачем мне нужно создать свойство, чтобы проверить, был ли ресурс удален при использовании одноразовой пясти в C#?

Для этого я должен реализовать интерфейс Microsoft IDisposable.

на основе Microsoft approach on implementing it, я должен сделать что-то вроде этого

общий интерфейс, который так

public interface ISomeClass : IDisposable 
{ 
    // ... some methods to include 
} 

public class SomeClass : ISomeClass 
{ 
    private readonly TimeTrackerContext _context; 

    private bool disposed = false; 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed && disposing && _context != null) 
     { 
      _context.Dispose(); 

     } 
     this.disposed = true; 
    } 

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

Я пытаюсь научиться C# правильный путь, так что я есть вопрос относительно этой реализации.

Вопрос

Почему мне действительно нужно иметь свойство, сказать мне, если объект был расположен или нет, прежде чем утилизировать его?

Другими словами, не могу ли я просто проверить, является ли значение _context нулевым, прежде чем удалять его? Что-то вроде этого

public class SomeClass : ISomeClass 
{ 
    private readonly TimeTrackerContext _context; 

    private void SelfDisposing() 
    { 
     if (_context != null) 
     { 
      _context.Dispose(); 
     } 

    } 

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

    private void SelfDisposing(bool disposing) 
    { 
     if (_context != null && !this.disposed && disposing) 
     { 
      _context.Dispose(); 
     } 

     this.disposed = true; 
    } 
} 
+6

Вы можете, но вы не устанавливаете контекст в null после его удаления, поэтому, когда он будет пустым, тогда? – Evk

+0

не будет делать это, поскольку весь объект больше не находится в памяти? –

+0

no, Dispose делает то, что вы говорите. Вы не устанавливаете контекст в значение null, поэтому оно не будет равно null. – Evk

ответ

6

_contextне будет null, если объект уже был удален. Он по-прежнему будет ссылаться на уже расположенный объект.

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

Что вы не должно на самом деле есть GC.SuppressFinalize В вашем объекте нет финализатора, поэтому нечего подавлять.

+0

Это объясняет! в отношении «GC.SuppressFinalize» Microsoft имеет следующее выражение в документах: ✓ DO реализует Basic Dispose Pattern и предоставляет финализатор типов, содержащих ресурсы, которые должны быть явно освобождены и у которых нет финализаторов. ' –

+2

@MikeA' on types удерживая ресурсы, которые должны быть освобождены явно. Вы не держите на каких-либо ресурсах, которые должны быть освобождены явно, поэтому вам не нужно финализатор (и в любом случае их нет). По всей вероятности, вам никогда не понадобится писать финализатор в вашей жизни, даже если вам нужно время от времени создавать одноразовые объекты, вам нужно только когда-либо делать это, потому что вы инкапсулируете другие одноразовые объекты. – Servy

+0

@MikeA См. [Этот ответ] (http://stackoverflow.com/a/2605502/2779530). Шаблон Microsoft Dispose предоставляется и должен быть надежным в ситуациях, когда каждый может получить свой класс и предоставить финализатор самостоятельно. В подобных ситуациях вполне нормально иметь вызов в GC-файле GC.SuppressFinalize в базовом классе, даже если он не имеет самого финализатора. Вы почти наверняка не нуждаетесь в этом (и, вероятно, должны сделать ваш класс «запечатанным», чтобы вообще не беспокоиться о наследовании). – Kyle

1

В поле (или свойство), указывающий, что Dispose уже называются не сильно требуется для реализации Dispose(bool) метода самих, как указано в следующем правиле:

✓ DO позволяют Dispose (BOOL) метод, который будет вызываться более одного раза. Метод может не делать ничего после первого вызова.

Это необходимо, если у вас есть другие методы/свойства, и вы хотите реализовать следующее правило из того же документа:

✓ DO бросить ObjectDisposedException от любого члена, который не может быть использован после того, как объект имеет были уничтожены.

+0

+1 Благодарим вас за эту информацию. Почему они используют метод 'virtual' вместо' override' здесь? Основываясь на моем понимании 'virtual', это позволит другому, но еще переопределить этот метод, в моем случае это должно быть' override' вместо этого? –

+1

Они используют 'virtual', потому что класс в примере является тем, который реализует шаблон. 'override' будет использоваться в классах, которые производят (наследуют) от этого класса (если таковые имеются). –

0

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

public sealed class SomeClass : ISomeClass 
{ 
    private readonly TimeTrackerContext _context; 

    public void Dispose() 
    { 
     _context.Dispose(); 
    } 
} 

Вторая мысль заключается в том, что само поле _context. Если вы передаете контекст через конструктор, вы на самом деле не знаете, действительно ли вам нужно его утилизировать. .NET Stream-based классы (такие как StreamWriter) также страдают от этой проблемы до .NET 4.0. И решение заключалось в том, чтобы написать Stream wrappers (например NonDisposableStreamWrapper), которые не располагают внутренним потоком, когда вызывается dispose. Действительно хорошим решением этой проблемы является добавление дополнительного объекта в конструктор, который указывает, следует ли распоряжаться внутренним классом или нет. Например:

public sealed class SomeClass : ISomeClass 
{ 
    private readonly TimeTrackerContext _context; 
    private bool _dispose; 

    public SomeClass(TimeTrackerContext context, bool dispose = true) 
    { 
     _context = context; 
     _dispose = dispose; 
    } 

    public void Dispose() 
    { 
     if (_dispose) 
     { 
      _context.Dispose(); 
     } 
    } 
}