-2

EDIT: So it looks like the only way to do this is with IDisposable. I suspected as much but it's still a bit of a bummer. I may make a class for this that takes an Action as @mjwills suggests but I still personally wish there was a way to do this without the using block. Alternative syntax like "using (var foo = new Bar([action]);" that makes the using statement apply to the block it's called from would be great.

In C# is it possible to perform an action when a variable goes out of scope? In C++ I would use an object's destructor to do something when it goes out of scope, but the garbage collector means I can't (cleanly) get the same behavior in C#. This is what I would write in C++:

class SetOnDestruction
{
    bool& m_thingToSet;
    bool m_newValueForThing;

public:
    SetOnDestruction(bool& thingToSet, bool newValueForThing)
        : m_thingToSet(thingToSet)
        , m_newValueForThing(newValueForThing)
    {

    }

    ~SetOnDestruction()
    {
        m_thingToSet = m_newValueForThing;
    }
};

void ExampleUsage()
{
    bool thingToSet = true;
    {
        SetOnDestruction setter(thingToSet, false);
        // do some stuff here
    }
    // thingToSet should now be false
}

I don't want to use IDisposable because then I'm wrapping my functions in big using blocks for what is really just a bit of syntactic sugar.

Does anyone know of anything relevant?

Dire Whale
  • 17
  • 1
  • 3
  • 1
    A finalizer is only called when the garbage collector collects it - which rarely happens right when the variable goes out of scope: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/destructors – UnholySheep Feb 20 '19 at 20:08
  • @UnholySheep I know, that's why the code I posted is for a C++ equivalent.... – Dire Whale Feb 20 '19 at 20:09
  • 3
    The unwanted by you IDisposable pattern is the only way – Selvin Feb 20 '19 at 20:13
  • 3
    `IDisposable` and a `using` block is the tool _designed_ to solve this problem - allow you to perform some action when an item is leaving scope. – gunr2171 Feb 20 '19 at 20:13
  • 1
    If you don't like IDisposable, `try`-`finally` might be useful... (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally) –  Feb 20 '19 at 20:14
  • @Selvin Well that sucks, but thanks for the info! – Dire Whale Feb 20 '19 at 20:14
  • also finalizer will not work as destructor in C++(while IDisposible would)... it will be not called (even if garbage collector would be collects) if you would keep reference to the object in other object wich would be "still alive" – Selvin Feb 20 '19 at 20:20
  • If you want it to *automatically* invoke the code in Dispose() when the variable goes out of scope, you have to use the using+IDisposable construct. Be sure to invoke Dispose(false) in your finalizer in case a caller instantiates your type without "using" and doesn't invoke Dispose. (see https://stackoverflow.com/a/1691850/1633949) – Richard II Feb 20 '19 at 20:21
  • Baked into .net is the idea that garbage collection happens when the runtime decides. If you need something to be "disposed" exactly when it goes out of scope, then fallback to C++ in a native library, for just that critical piece – Richard II Feb 20 '19 at 20:30
  • @mjwills IDisposable is very useful for what it's meant for, but for my particular use case in asking this question it's overkill. I'm just looking to ensure some class variables are reset after a function exits. With multiple returns that means placing the same "m_aVariable = false" in multiple places. I could get the same behavior with IDisposable but I fear the big using blocks aren't worth it. – Dire Whale Feb 20 '19 at 20:37
  • @DireWhale Honestly, the fact that you want to "reset the variables every time this scope ends" means that the variables are set to the wrong scope. The variables should be *local to that scope* to whichever scope you want them to be cleared when they leave, so that the variables *can't be accessed* when they don't make sense, rather than trying to constantly clear and reset a variable set to too high of a scope. – Servy Feb 20 '19 at 20:43
  • @Dire, you keep mentioning fear of "big using blocks". if your function is considered too big with 3 additional lines added, then it was probably too big to start with. at any rate, it sounds like you *might* have a code smell, if you need to set variables to certain states only when the object out of scope. I say *might* because I don't know all of your requirements....just food for thought. – Richard II Feb 20 '19 at 20:55
  • One of the challenges one has when switching from one language to another is thinking purely in terms of their old language. As you get more comfortable with C#, you'll spot better _overall_ patterns, rather than just thinking in terms of C++ patterns in C# syntax. – mjwills Feb 20 '19 at 21:00
  • @Servy The variables I'm resetting are bools that indicate an operation's occurring so that another call to the same or similar method will return immediately. It's to prevent multiple button presses from causing chaos. Does it sound like I am doing something wrong? – Dire Whale Feb 20 '19 at 21:28
  • 2
    @DireWhale Probably, yes. My first instinct would be, if the application is multithreaded, to use a monitor with TryEnter, because a boolean won't be safe from multiple threads. As mentioned earlier, that'd also be a good situation to call a method accepting a delegate, which then may or may not actually call it based on it's own state of whether or not something is running, so that the logic to keep track of what's running is in one place, not in every different action that could be run. – Servy Feb 20 '19 at 21:33

1 Answers1

-1

If you're trying to destroy unmanaged resources we can in fact use a destructor such as

~SetOnDestruction()
{
  /* destruction things */
}

However, this gets called when the garbage collector destroys the object and thus is non-deterministic.

The only other way is using IDisposable More info on IDisposable from MSDN here

Edit: In honor of the down votes (And pure interest) Let's see not if we should do this but if we can do this

We could hypothetically enforce local scope by calling something like this

    class Program
    {
        static void Main(string[] args)
        {
            LocalScopeWrapper();
        }

        private static void LocalScopeWrapper() // maybe I could have a better name?
        {
            var testObj = new TestObj();
            testObj.Print();
            Console.WriteLine($"Total memory used before forced collect {GC.GetTotalMemory(false)}");
            testObj = null;
            GC.Collect(0); // Generation 0, lets only clear the smallest Small Object Heap
            Console.WriteLine($"Total memory used after forced collect {GC.GetTotalMemory(true)}");
        }

        private class TestObj
        {
            public void Print()
            {
                Console.WriteLine("HelloWorld");
            }
        }
    }

Here we're forcing the object to be collected after we set it to null. Makes sense, but not exactly what we want, and as Dire Whale asked for not clean.

But, I wanted to see if we emulate C++ behavior, and it is possible.

We could use Pinvoke and handles to pass our object to C/C++ libraries and call free as shown in this great answer by @xanatos

I was curious how bad we could get in C#, and this seemed like the ultimate of getting around the GC by letting a Pinvoke delete things under our nose. It isn't useful but interesting none the less.

Jlalonde
  • 483
  • 4
  • 13
  • The only other way is IDisposable than the destructor, Is there a reason you need C++ capabilities in C#, and is it worth it? – Jlalonde Feb 20 '19 at 20:14
  • It's not a need, it's just a convenience. I usually use the early exit pattern, but that can mean setting values back to their default at each return statement. I would prefer the convenience of creating out-of-scope behavior. – Dire Whale Feb 20 '19 at 20:18
  • 1
    The answer is telling you what you can do. You can't do what you want to do. Something going out of scope isn't an event to which you can respond. – Scott Hannen Feb 20 '19 at 20:21
  • 2
    There is very little need to use a destructor in .Net, and there are many many reasons why you should not, In fact if you think you need a destructor you are probably doing something wrong – TheGeneral Feb 20 '19 at 20:29
  • C# doesn't have destructors, it has **finalizers**. They are *not* the same thing. https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-334%203rd%20edition%20June%202005.pdf , section 17.12 – Daniel Mann Feb 20 '19 at 21:04
  • @DanielMann If you look at [the C# langauge specs](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/introduction) it uses the term "destructor", not "finalizer". So yes, C# *does* have destructors. – Servy Feb 20 '19 at 21:26
  • @Servy ECMA 334 says otherwise. *In the previous version of this standard, what is now referred to as a "finalizer" was called a "destructor". Experience has shown that the term "destructor" caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++.* We both gave links that say different things, so who knows which is the overriding authority. – Daniel Mann Feb 20 '19 at 22:15
  • @DanielMann Yes, *ECMA* calls it a finalizer. *C#* calls it a destructor. So the statement that *C#* has destructors is accurate, and the statement that it doesn't, is false. – Servy Feb 20 '19 at 22:40