Remix.run Logo
brewmarche 3 hours ago

In C# the closest analogue to a C++ destructor would probably be a `using` block. You’d have to remember to write `using` in front of it, but there are static analysers for this. It gets translated to a `try`–`finally` block under the hood, which calls `Dispose` in `finally`.

    using (var foo = new Foo())
    {
    }
    // foo.Dispose() gets called here, even if there is an exception
Or, to avoid nesting:

    using var foo = new Foo(); // same but scoped to closest current scope
These also is `await using` in case the cleanup is async (`await foo.DisposeAsync()`)

I think Java has something similar called try with resources.

cogman10 41 minutes ago | parent | next [-]

Java's is

    try (var foo = new Foo()) {
    }
    // foo.close() is called here.
I like the Java method for things like files because if the there's an exception during the close of a file, the regular `IOException` block handles that error the same as it handles a read or write error.
mort96 19 minutes ago | parent [-]

What do you do if you wanna return the file (or an object containing the file) in the happy path but close it in the error path?

cogman10 12 minutes ago | parent [-]

You'd write it like this

    void bar() {
      try (var f = foo()) {
        doMoreHappyPath(f);
      }
      catch(IOException ex) {
        handleErrors();
      }
    }

    File foo() throws IOException {
      File f = openFile();
      doHappyPath(f);
      if (badThing) {
        throw new IOException("Bad thing");
      }
      return f;
    }
That said, I think this is a bad practice (IMO). Generally speaking I think the opening and closing of a resource should happen at the same scope.

Making it non-local is a recipe for an accident.

*EDIT* I've made a mistake while writing this, but I'll leave it up there because it demonstrates my point. The file is left open if a bad thing happens.

mort96 22 minutes ago | parent | prev [-]

That approach doesn't allow you to move the file into some long lived object or return it in the happy path though, does it?

actionfromafar 13 minutes ago | parent [-]

As someone coming from RAII to C#, you get used to it, I'd say. You "just" have to think differently. Lean into records and immutable objects whenever you can and IDisposable interface ("using") when you can't. It's not perfect but neither is RAII. I'm on a learning path but I'd say I'm more productive in C# than I ever was in C++.

mort96 4 minutes ago | parent [-]

I agree with this. I don't dislike non-RAII languages (even though I do prefer RAII). I was mostly asking a rhetorical question to point out that it really isn't the same at all. As you say, it's not a RAII language, and you have to think differently than when using a RAII language with proper destructors.