-3

If I use the following structure:

public class TestClass : IDisposable
{
    private SqlBulkCopy _bulkCopy;
    public TestClass(SqlConnection connection)
    {
        _bulkCopy = new SqlBulkCopy(connection);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_bulkCopy != null)
                _bulkCopy.Dispose(); // Cannot call dispose as this is a concrete implementation
        }
    }
}

I cannot access the dispose function on my _bulkCopy object.

I know I can use a using statement, but is that the only way?

I would prefer not to as this means I may have to keep recreating this object

I know I could also wrap an interface around this, but is there any other way?

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
Alex
  • 3,730
  • 9
  • 43
  • 94
  • When you say, "I cannot access the dispose function on my _bulkCopy object.", is that a compiler message? – Bernard Vander Beken Jun 04 '20 at 15:19
  • *I cannot access the dispose function on my _bulkCopy object.* why? – Selvin Jun 04 '20 at 15:19
  • 4
    Cast it to IDisposable? – the.Doc Jun 04 '20 at 15:19
  • `void Dispose(bool disposing)` when would this boolean ever be false? If you're not disposing, you shouldn't be calling the `Dispose` method. – Flater Jun 04 '20 at 15:20
  • @Flater https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose – Heretic Monkey Jun 04 '20 at 15:21
  • @BernardVanderBeken no it is because it is a concrete implementation and so the class does not have this method. – Alex Jun 04 '20 at 15:21
  • @Selvin see my comment above – Alex Jun 04 '20 at 15:21
  • 5
    `SqlBulkCopy` implements the interface explicitly so you have to cast to `IDisposable`: `((IDisposable)_bulkCopy).Dispose()`. – Lee Jun 04 '20 at 15:22
  • 2
    @Selvin, as Lee has elaborated on my comment, the method does 'exist' but it has been explicitly implemented so a cast is required. – the.Doc Jun 04 '20 at 15:23
  • 1
    @Lee as long as it's `System.Data.SqlClient.SqlBulkCopy` not some *concrete implementation* whatever it means – Selvin Jun 04 '20 at 15:23
  • 2
    It's "how to call an explicitly implemented interface method given a reference to the implementing class." – madreflection Jun 04 '20 at 15:24
  • 1
    Reproduced it and the error was: Compilation error (line 31, col 27): 'System.Data.SqlClient.SqlBulkCopy' does not contain a definition for 'Dispose' etc. Lee's comment fixes this – Bernard Vander Beken Jun 04 '20 at 15:25
  • @the.Doc if you want to add that as answer I will mark it as correct. – Alex Jun 04 '20 at 15:26
  • @Lee same comment as above for you – Alex Jun 04 '20 at 15:26
  • 1
    duplicate https://stackoverflow.com/questions/18338425/can-an-idispose-objects-not-have-an-available-dispose-method or https://stackoverflow.com/questions/3118861/how-does-this-class-implement-idisposable-if-it-doesnt-have-a-dispose-method ... which are first result for "stackoverflow IDisposable doesn't have a Dispose method" – Selvin Jun 04 '20 at 15:31

1 Answers1

3

This can happen when an interface is explicitly implemented. First, a basic example of an implicitly implemented interface:

public interface IFoo
{
    void FooTheBar();
}

public class ImplicitImplementer : IFoo
{
    public void FooTheBar()
    {
        // ...
    }
}

This can be used the way you expect it to, both by its concrete type and the interface:

ImplicitImplementer a = new ImplicitImplementer();
a.FooTheBar(); // works

IFoo b = new ImplicitImplementer();
b.FooTheBar(); // works

But when you explicitly implement an interface, you must use the interface type.

public class ExplicitImplementer : IFoo
{
    public void IFoo.FooTheBar()  // Notice the "IFoo."
    {
        // ...
    }
}

Notice the consequences:

ExplicitImplementer a = new ExplicitImplementer();
a.FooTheBar(); // ERROR!

IFoo b = new ExplicitImplementer();
b.FooTheBar(); // works

That's just how it works. I suspect that your SqlBulkCopy class explicitly implements IDisposable, which means you're going to have to cast it to the correct interface:

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_bulkCopy != null)
            (_bulkCopy as IDisposable).Dispose();
    }
}    

I prefer the as syntax, but you can use (IDisposable) _bulkCopy if you prefer. You can actually slightly improve the flow of the code here:

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        (_bulkCopy as IDisposable)?.Dispose();
    }
}

This prevents exceptions both in the case of _bulkCopy being null or _bulkCopy no longer implementing IDisposable anymore. It disposes if it can, and otherwise it does nothing.


It may seem weird why this is ever useful, and it doesn't quite seem necessary in your case. Explicit implementation is only useful when a class implements multiple interfaces that have a clashing interface member, e.g.:

public interface IFoo
{
    void FooTheBar();
}    

public interface IBar
{
    void FooTheBar();
}

public class FooBar : IFoo, IBar
{
    public void FooTheBar()
    {
        Console.WriteLine("IFoo or IBar?");
    }
}

This code actually works, but the same method will be called regardless of whether you do:

IFoo a = new FooBar();
a.FooTheBar(); // "IFoo or IBar?"

IBar b = new FooBar();
b.FooTheBar(); // "IFoo or IBar?"

But what if you want these two methods to be separate? Well, then you explicitly label each method implementation as belonging to a specific interface. That's what explicit implementation is.

public class FooBar : IFoo, IBar
{
    public void IFoo.FooTheBar()
    {
        Console.WriteLine("IFoo");
    }

    public void IBar.FooTheBar()
    {
        Console.WriteLine("IBar");
    }
}

And then you'll see:

IFoo a = new FooBar();
a.FooTheBar(); // "IFoo"

IBar b = new FooBar();
b.FooTheBar(); // "IBar"

But since you've restricted these methods to the specific interfaces, FooBar itself cannot resolve to a specific FooTheBar method anymore, hence the error you're faced with. It's a consequence of a solution to another problem (i.e. overlapping interfaces).

Flater
  • 12,908
  • 4
  • 39
  • 62