1

I am using a 3rd party class, Table, that represent a database table. The class has a Close() method and it implements IDispose.

I find that calling Close() many times on such a table is fine. Likewise calling Dispose() many times is also fine.

However if I call Dispose() I can not call Close() again or I will get a ObjectDisposedException.

I want to use such a Table as a private member variable in a class.

Samples from the provider of the Table class do not call Dispose() on the table. However since calling Dispose() followed by Close() causes a crash I take it that Dispose() does a full cleanup?

I therefore conclude that I must call Close() followed by Dispose() once and only once?

What is the best way to achieve this? Should I let my class implement IDispose and use the Dispose pattern with a bool disposed_ variable that ensures that the cleanup is only done once + a GC.SuppressFinalize in the Dispose method?

I have already implemented this pattern and understand how it works.

However I am baffled on how complex this is. I would think that C# code would be simpler than C++.

Is there another simpler/better way to do this?

Andy
  • 3,251
  • 4
  • 32
  • 53
  • 4
    `I therefore conclude that I must call Close() followed by Dispose() once and only once?` In almost every type that has a `Close` method, calling `Close` and calling `Dispose` does the same thing. – mjwills Aug 22 '19 at 13:52
  • 4
    `Should I let my class implement IDispose and use the Dispose pattern with a bool disposed_ variable that ensures that the cleanup is only done once + a GC.SuppressFinalize in the Dispose method?` Yes. Although you likely can remove the `GC.SuppressFinalize` since your class almost certainly doesn't need a finalizer. – mjwills Aug 22 '19 at 13:52
  • 5
    `I want to use such a Table as a private member variable in a class.` I'd strongly encourage you to use an ORM like Dapper, so that instead of storing tables you store `List`. Then the `Dispose` problem becomes a non-issue. – mjwills Aug 22 '19 at 13:55
  • @mjwills: I would also think that calling Close() and Dispose() did the same thing. However I can Close() after having already called Close() but not after having called Dispose() so clearly they do not do exactly the same thing. – Andy Aug 22 '19 at 13:59
  • 2
    By the way, since you mention `GC.SuppressFinalize`, unless your class manages unmanaged resources (for example, I/O that isn't using .NET wrappers), your class _**should not**_ implement a _Finalizer_ (aka a C# _destructor_). If you don't have a Finalizer, you don't need to call `GC.SuppressFinalize` – Flydog57 Aug 22 '19 at 13:59
  • `so clearly they do not do exactly the same thing` I'll be more precise - they _effectively_ do the same thing (if called only once). – mjwills Aug 22 '19 at 14:00
  • 2
    By the way, the reason you see the `ObjectDisposedException` is because another thing about `IDispose` is that once an object is disposed, you are not supposed to call any other methods than `Dispose`. As a result, framework derived classes track the disposed state of a class and throw that exception if you call any of the class's methods after disposal. – Flydog57 Aug 22 '19 at 14:15
  • @Flydog57: But does it hurt to call GC.SuppressFinalize even if there is no finalizer? I am not writing any destructor (~) method. In C++ this would imply that one would be created silently for me. But not in C#? So no ~ method=> no finalizer? – Andy Aug 22 '19 at 14:21
  • 1
    Finalizers in C# are special. Unlike C++ (where you get a free destructor if you don't write one), most (/nearly all) C# classes have no Finalizer. If a class has a finalizer, objects of that type are registered for finalization when they are created (that SuppressFinalize call removes that registration for that object). Writing (& testing) a good/safe Finalizer is very hard. When a Finalizer runs, it greatly complicates the already complicated GC dance. Check out Chris Brumme's 2004 blog on Finalizers (https://devblogs.microsoft.com/cbrumme/finalization/) to get a better idea. – Flydog57 Aug 22 '19 at 14:36

1 Answers1

0

Hi Andy a good example is here IDisposable Interface It contains how to dispose managed and unmanaged resource.

If we replace the Table3rd with a MemoryStream It has Close and Dispose methods. We can do this implementation Fiddle

using System;
using System.IO;

public class Table3rd : MemoryStream{
};
public class MyTable : IDisposable
{
    public Table3rd Data { get; private set; }

    public MyTable(){
        Data = new Table3rd();
    }

    private bool disposed;

    public void Close()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    public void Dispose(bool disposing)
    {
        if (!disposed)
        {
            try
            {
                if (disposing)
                {
                    Data?.Dispose();
                    disposed = true;
                }
            }
            finally
            {
                Dispose(disposing);
            }
        }
    }

    public void Dispose()
    {
        Close();
    }
}

public class Program
{
    public static void Main()
    {
        var table = new MyTable();
        var data = table.Data;
        var writer = new StreamWriter(data);
        writer.Write("Table data");
        writer.Flush();
        data.Position = 0;
        var reader = new StreamReader(data);
        var mensaje = reader.ReadToEnd();
        Console.WriteLine(mensaje);
        // Too many dispose
        table.Close();
        table.Dispose();
        table.Close();
        table.Dispose();
    }
}

I hope it will be usefull for you.