-2

I have this code.

public class Application
    {
        HybridObject obj;

        public Application()
        {
            obj = new HybridObject().Core;
            obj.Delete();

            while (true)
            {
                Update();
            }
        }

        void Update()
        {
            if (obj == null) Console.WriteLine("Destroyed"); // I NEED THIS TO WORK
            if (obj.Core == null) Console.WriteLine("Destroyed Core"); // THIS WORKS
        }
    }
public class HybridObject
    {
        protected HybridObject core;
        public HybridObject Core => core;

        public HybridObject()
        {
            this.core = this;
        }

        public void Delete()
        {
            this.core = null;
        }
    }

What I'm trying to achieve is... In the Application class constructor, I create a new HybridObject instance and then I want to set the obj property to the Core property from the HybridObject instance not the HybridObject itself. This way, when obj.Delete() is called, it will set the Core property of the HybridObject to null, and the obj property in Application will also reflect this change.

But I can't get this to work and I'm really struggling. I hope this makes sense.

Any code examples would be super as I've been at this most of the day with no solution

Thank you!!

LJH
  • 82
  • 6
  • 2
    What is your objective here? C# is garbage collected and objects will be destroyed automatically when they go out of scope. For things that are using unmanaged resources and need to release those resources when they are destroyed, implement the `IDisposable` pattern and make use of `using` blocks to ensure deterministic cleanup. – Daniel Mann Apr 14 '23 at 21:07
  • GC / Destroying objects! I understand that. But the object will never be garbage collected because Application has a strong reference to HybridObject from the obj property. But if I could set all references to null, by just setting the Core property to null. The object then has no references and will be GC. Its not just Application that will be creating these objects, it can be any class, so I can't just run through Application and make things null, I can't even set the obj = null; in Application because I need to avoid that, the Delete method needs to make all references null. – LJH Apr 14 '23 at 21:19
  • Extra information: I don't want to use weakreferences, I know I can pass in a ref to the obj and set that to null, but that still isn't what I need because it will only set the one obj property to null. If I have Application.obj set up as I explained that uses the Core property over the HybridObject instance, I could then pass the obj property into things such as lists etc, then when I set the Core property to null. All references would be set to null no matter where they exist. Is this even possible? – LJH Apr 14 '23 at 21:22
  • 1
    I don't think it is possible. Neither do i think it should be possible. It's a really bad idea. – Fildor Apr 14 '23 at 22:09
  • @Fildor I understand it may not be a great idea. But what other options do I have in my given situation? – LJH Apr 14 '23 at 22:11
  • The only solution I can even think of is using reflection to check all class instances and check for properties that are HybridObject if they match set them to null. But then I would have to check every possible way you can store an object, collections, properties, structs, etc... its just not really feasible. This approach would also be really slow for a real-time application that needs performance. – LJH Apr 14 '23 at 22:26
  • Sounds like you need to solve this in another way. What is the significance of setting a local reference to nul? Why can't the caller check if _their_ `obj.Core == null`? – CodeCaster Apr 14 '23 at 22:49
  • Yes that works as explained in the example above. But I'm trying to avoid using Core all together and just use obj. The issue is of course, the HybridObject will never be garbage collected because obj has a strong reference to it – LJH Apr 14 '23 at 23:07
  • Let's just assume for one moment this would work. How would you prevent the nulling in a case where the instance is still needed? How would the caller of delete() know? – Fildor Apr 15 '23 at 05:59
  • It wouldn't be needed, once it's deleted that's it in terms of the obj property. The HybridObject would still exist, but there would just be no references to it anymore because they all got set to null, then the garbage collector would collect the HybridObject. I would just be doing checks constantly to check if null or not or do you mean somewhere out of my control? – LJH Apr 15 '23 at 10:04
  • I added some more information to my answer. – Flydog57 Apr 16 '23 at 03:48

1 Answers1

2

First, you have this code:

public class Application
{
    HybridObject obj;

    public Application()
    {
        obj = new HybridObject().Core;
        obj.Delete();

        while (true)
        {
            Update();
        }
    }

    void Update()
    {
        if (obj == null) Console.WriteLine("Destroyed"); // I NEED THIS TO WORK
        if (obj.Core == null) Console.WriteLine("Destroyed Core"); // THIS WORKS
    }
}

Your obj is a private member field of your Application class. It's never going to go null unless you set it to null. It doesn't matter if you call Delete on it, it will forever reference the object you told it to reference (until you tell it to reference another object (via assignment) or set it to null (also by assignment).

Then you have this rather strange class:

public class HybridObject
{
    protected HybridObject core;
    public HybridObject Core => core;

    public HybridObject()
    {
        this.core = this;
    }

    public void Delete()
    {
        this.core = null;
    }
}

An instance of this class can have two states, normal or deleted. Your Core property (and its backing core field) can either point to the object as a whole (the normal (or newly constructed) state, or it can be set to null (the deleted state). That's a bit odd, but fine.

BUT, in either state, once there are no longer any references to an instance of your HybridObject class, that particular instance becomes eligible for garbage collection. It doesn't matter that in the normal state, that instance will hold a hard reference to itself (since objects that are eligible for collection do not act to keep objects they refer to alive). Once an object is eligible for collection, it will be collected at the next GC (give or take a few complicated rules that don't affect the logic of your program).

This kind of cyclical reference was a real PITA in languages like C or C++ (where you had to carefully walk the graph of objects you created in order to delete them), or in reference-counted frameworks like the COM-based (pre-.NET) Visual Basic. The .NET GC eats these situations for breakfast.

Just accept that you don't need to worry about deleting everything (in the much greater than 99% case) and live the garbage collected lifestyle.

Update (15-Apr-2023)

You mention "real-time" and "game" in your comments. My advice to you is to forget what you know about managing memory and objects (which I'm guessing you got from experience from C, C++ or some other traditional language).

Then read up on how the .NET garbage collector works, and how managed memory in general works. In particular, bone up on:

  • The generational garbage collection (Gen0, Gen1 and Gen2)
    • In particular the cost of long-lived objects
  • The large object heap
  • The GC.TryStartNoGCRegion call (and its friends).
  • Finalizers (and why you should avoid having them ever run if possible)
  • The Dispose pattern

In many ways, a GC-driven program can be faster than a traditional heap program. Memory allocations take no locks on Intel processors, and the only real cost is that the Framework guarantees that memory is zeroed before your program sees it. You also don't need to do any work to clean up any memory-only resources.

Consider a game, where the game object opens a scene object (or a level object or whatever). Within the scene object the program builds a complex graph of thousands of possibly inter-related objects, with lots of hard references between the objects. When the player completes the level/scene/whatever, and you want to tear that all down, all you need to do in the main game code is currentScene = new NextScene(creation, parameters); Since currentScene no longer points the previous scene (and, nothing else points to it), the old scene - and all of its contents - are now eligible for collection (without you having to ever call Delete or something similar on anything).

The cost for this is garbage collection. You don't have to write any code to make it happen, but it does happen. It runs on a separate thread, and it runs at non-deterministic times. If you are writing something with soft-real-time constraints, you need to be thinking about this and how to coerce it to run when it will least affect your application.

For example, although just about everything you read about the GC says "you should almost never call GC.Collect", if you have a situation like what I described above (with a game object and an abandoned scene object), this is a good example of a time when GC.Collect makes sense. When you abandon the previous scene, your program is in a state where responsiveness is likely less important, and where you know that there are a ton of objects ready to be collected - that's when a program induced collection make sense.

My point is that if you are writing a game or a soft-real-time system, you really need to have a firm grasp of the garbage collector and the whole managed memory system.

Flydog57
  • 6,851
  • 2
  • 17
  • 18
  • I understand this code doesn't make much sense, I was just trying to get the point across in what I'm trying to achieve. I understand what you're saying but this still isn't valid for what I'm trying to do.. Ignore everything but the Application class for a moment, If I wanted to do obj = new HybridObject(); then "Delete" obj. I need obj to become instantly null and everything else that points to the HybridObject. Theoretically I need to remove the object instantly. In a game engine you WOULD need this, garbage collection alone isn't good enough that's why people use C++ – LJH Apr 14 '23 at 22:46
  • I think stating I don't understand how this works is a bit bold to say. I know how garbage collection works, it will NOT remove the object when I want it to remove the object, it runs on its own accord periodically. even if I force GC.Collect() It will NEVER remove objects that have strong references. You can use WeakReferences yes. I'm unsure if it's the way I'm explaining this but I need to set all reference pointers to a specific object to null from a method inside that object, without manually tracking references, but that's just a huge mess. It's just not possible. The code was an example – LJH Apr 14 '23 at 22:52
  • Simple solution (and _only_ solution) to making `obj` be null is `obj=null;`. As I said, it's effectively a private field of your class; only code in that class can make it be null. You can make your object be _deleted like_ by having a flag that says _I've been deleted_ and then refusing to do anything. But, in a GC world, if you want an object to act deleted, just set the last reference to the object to null. The object still may exist on the heap, but since no one can reference it, who cares – Flydog57 Apr 14 '23 at 22:53
  • Sorry, no insult intended. My _you don't understand_ comment was related to the fact that you somehow expected that the variable `obj` would somehow have its value changed as a result of you calling a method on the object to which `obj` refers. That's not how variables (effectively reference type object references) work – Flydog57 Apr 14 '23 at 22:57
  • No it's completely fine, I've been at this all day. I've tried so many things! I know it doesn't make sense it was just an example to explain the sort of behaviour I'm wanting but obviously I don't know how to do what I'm wanting. I'm just struggling to understand how I can do this, I've actually got it working using reflection, but its slow and it's a huge mess. It sets all references that point to the HybridObject to null, which then allows the garbage collector to destroy it eventually, this is what I'm wanting. But its really slow! – LJH Apr 14 '23 at 23:03
  • BTW, the only strong reference in your code is the one in the `obj` variable. Yes, it will keep your `HybridObject` instance alive forever, but that's because of the infinite loop around your `Update` method. That you have a cyclical reference in your class doesn't keep instances of your class alive; once you object is not rooted (or referred to by a rooted reference (directly or indirectly)), it's eligible for collection. I've been using (and teaching) .NET for >20 years (including in a soft-realtime system (once)), and I may have used a weak reference (outside of a demo) once – Flydog57 Apr 14 '23 at 23:05
  • Yes I know obj is the only strong reference, that's why I'm trying to set it to null without just using obj = null. Because obj could have been passed around into another class, stored in a list, or be used as a value for another HybridObject property for example. Just using obj=null wouldn't then set all those to null because now they have strong references, I hope this explains my point better – LJH Apr 14 '23 at 23:11
  • 3
    @LJH Go back to the start. Explain the *problem* you're trying to solve by doing this. This is a classic X/Y problem. You have Problem X. You've decided to implement Solution Y, so you're asking how to implement it. But Solution Y isn't an appropriate solution to Problem X. You're saying "this code doesn't really demonstrate the problem". Then *show us the real code*. – Daniel Mann Apr 15 '23 at 01:44