简体   繁体   中英

Making a managed object truly deletable

Background:

I tried asking this question yesturday but with no real conclusion, so I thought I would try asking it in a different way as this problem is causing me a big headache.

My application is an embedded HMI design tool. This allows the user (who are internal developers) to visually design how the embedded HMI will look and then, with a button click, generates the C code. Most of this now works.

One of the main things the tool does is allows the user to create new screens (which can then have controls dropped on them). In my application a 'screen' is an object. When a new screen is added it is placed in a List<Screen> which is later used to generate the C code.

One of the properties of that screen is the private Screen nextScreen which is a reference to another screen object which indicates what screen to show next when the navigation button is pressed. Again all this works great.

Problem:

However, when playing with the tool to generate code we found a problem. 2 screens were created, with the first screen having its nextScreen property set to the 2nd. A bit later it was decided that the 2nd screen was wrong so it was deleted and a new screen created. However they forgot to set the first screen nextScreen property to this new screen, so it was still referencing the original screen which, visually had been deleted.

When the code was generated the resulting C code failed to compile as the first screen was referencing a screen which, within the generated C environment, did not exist.

Question:

So my question is how can I best implement a way of deletion which ensures that anything that references an object being 'deleted' knows about it and sets that property to null?

Thoughts so far:

1) I have tried creating the 'nextScreen' property as a WeakReference which does work but it means I have to manually call the GC.Collect() every time a delete is done... not ideal from my understanding! (My example of trying this can be found in the link at the top of this post)

2) I could, upon deletion, check all objects for a reference to the one being deleted and set it to null. However what I have described above is just one of the many objects which make up the HMI, most of which have one or more references to other objects. So this could be slow and in my mind just doesnt seem right.

3) Maybe use IDisposable??? but from my understanding this is for unmanaged code!

Any ideas or areas for me to read up on would be great as I'm now pulling my hair out over this... not that I have much!!!

我认为您可以使用ObjectPool模式创建和删除对象以及该对象上的某些Wrapper,以控制其生存时间

One of the fundamental design imperatives of the GC is if there is any means by which code might encounter a reference to an object, the object will continue to exist. There is no way to "delete" an object such that references to it will become invalid. There are, however, two things you can do:

  1. Have your class include a field which indicates whether it has been invalidated, and have code which receives or follows a reference to a class instance, check that field to ensure the instance is still valid. If code sets that field within an instance, any references to it will no longer be references to a valid instance, but will (by definition) become references to an invalidated instance.

  2. If you create a WeakReference to an object, the Target property of that WeakReference will become null when the GC detects that no non-weak references to the target exist (if a reference to a non-null target is copied to another variable, that variable will keep the target alive even if the weak reference had been the only thing in the universe with a reference to the target). Forcing a full garbage-collection cycle will generally invalidate immediately any weak references to objects which aren't strongly referenced anywhere, but would represent something of an abuse of the GC system.

  3. Implement a "find all used objects" routine which counts how many times it has been run, and have a field within each class instance into which that routine will store its counter. Any object which should be considered "used" will hold the value the "find all used objects" routine counter held the last time it was run. This is somewhat like what the GC does with objects it knows about, but if you do this yourself you can decide what forms of reference should justify regarding an object as "used".

You could make a new generic class called Deletable<T> to wrap your objects. It would work similar to Nullable<T> in that it contains a property of type T that holds the original value. It would need a readonly bool property IsDeleted that indicates if the wrapped value has been deleted, and a Delete method that NULLs out the wrapped value, and sets the IsDeleted flag.

Everything that uses Screen objects would switch to Deletable<Screen> . When a screen gets deleted, call Delete on the wrapper. Any code that cares if a screen is deleted can just check scr.IsDeleted before using scr.Value .

This will allow garbage collection to clean up any large objects, while leaving a nice small "flag" value in its place that you can use to check to see if the real object has been deleted.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM