0

I have a class Foo which contains a pointer to unmanaged memory from C dll. It implements IDisposable to free unmanaged memory using C dll and some other functionality with that ptr using dll.

public unsafe class Foo : IDisposable {
    void* ptr;

    internal Foo() { 
        /* initialize ptr from C dll */
    };

    public int getInfo() {
        /* return info from ptr using dll */
    }

    public Dispose() {
        /* free ptr using C dll */
    }
}

Also I have a factory which returns an exemplar of Foo object:

public unsafe class FooFactory {
    public Foo createFoo() {
        return new Foo();
    }
}

Finally I have a function which takes Foo as argument, processes it and returns:

public Foo process(Foo f) {
    /* do some stuff with f */
    return f;
}

All I want to do is to prevent user from creating Foo object, storing it in a variable and passing that variable to a process with saving result to another variable, because it may be unsafe in such example:

Foo f2 = null;
using (var f1 = FooFactory.createFoo()) {
    // inside one can work with f1 and f2
    f2 = process(f1);
}

// but outside f1's ptr is already destroyed in C dll and f2 can not use it
f2.getInfo();

I want them use only on of these approaches, as they are safe:

using var f1 = FooFactory.createFoo();
f1 = process(f1);

// or
using var f1 = process(FooFactory.createFoo());

In C++ I can do it using std::unique_ptr as it is not copyable. Is there any way to do it in C#?

elo
  • 489
  • 3
  • 13
  • A notion of ownership can't be implemented in C# since you can't prevent copying. What you can do is make the class track whether it has been disposed (set a field, using `Interlocked.CompareExchange` for free thread safety), and have all methods/property accesses that would be unsafe throw `ObjectDisposedException` if they're used after disposing. This does not prevent the unsafe thing from happening, but is still better than (say) relying on memory access exceptions (including null reference exceptions). Simple use-after-disposed scenarios (same method) can be detected by a static analyzer. – Jeroen Mostert Sep 16 '21 at 14:22
  • For correctly implementing the IDisposable, visit [this](https://stackoverflow.com/a/538238/259206) famous answer (especially if you decide to put finalizer into the game). – Borislav Ivanov Sep 16 '21 at 14:32

2 Answers2

2

Nothing comes to mind to exactly match that pattern.

Instead, I would expect the Foo type to raise an ObjectDisposedException if I tried to use f2 after leaving the using block. Checking status and throwing that exception might be one of the first responsibilities of the getInfo() (and similar) methods. The using pattern is pretty-strongly engrained among C# developers, where no one writing this code would be surprised by this behavior.

This is also one of the rare cases where you may want to implement a finalizer for the C# type.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
1

You can't forbid that, but you can make the Foo class safer by adding some code:

public unsafe class Foo : IDisposable
{
    void* ptr;

    internal Foo()
    {
        /* initialize ptr from C dll */
    }

    public int getInfo()
    {
        if (ptr is null) { // <==== test whether ptr is still valid!
            return 0; // or throw an exception.
        }
        /* return info from ptr using dll */
    }

    public void Dispose()
    {
        /* free ptr using C dll */
        ptr = null; // <==== set ptr = null when disposed!
    }
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188