What is the best way to remove all references of an object?

R

Robert Zurer

I have a large pool of business objects all referencing one another in
various ways.
In the client application I want to do something like

employee.Delete();

Behind the scenes, I want to remove all references to the object
allowing it to be garbage collected. I then want to remove the physical
representation of the object from persistance. Unless I have missed a
scenario, the references to the object can be deleted in two ways

MyParentObject.EmployeeProp = null; or
MyEmployeeList.Remove(employee);

In order to do this, I have to maintain some kind of internal lists of
these relationships.

My questions are --

1) Have I missed any other way that an object can be referenced?
2) Isn't there an easier way? Doesn't the GC already have this list
somewhere?



Robert Zurer
(e-mail address removed)
 
J

Jon Skeet [C# MVP]

Robert Zurer said:
I have a large pool of business objects all referencing one another in
various ways.
In the client application I want to do something like

employee.Delete();

Behind the scenes, I want to remove all references to the object
allowing it to be garbage collected. I then want to remove the physical
representation of the object from persistance. Unless I have missed a
scenario, the references to the object can be deleted in two ways

MyParentObject.EmployeeProp = null; or
MyEmployeeList.Remove(employee);

In order to do this, I have to maintain some kind of internal lists of
these relationships.

My questions are --

1) Have I missed any other way that an object can be referenced?

Well, the second case is really just the same as the first - it's just
that the Remove method will do appropriate things to the list object so
that it does the first thing internally.
2) Isn't there an easier way? Doesn't the GC already have this list
somewhere?

No, not necessarily. The GC doesn't have to know *all* the references
at any time, it only needs to be able to spot at the end of a sweep if
there are *no* live references left. As soon as it's seen that there's
*one* live reference, it doesn't need to look at any others.

This is basically a nasty situation to be in - one thing you *could* do
instead would be to have a flag within the object which said it was
basically no longer useful, and make sure that everything you do with
the object checks that flag before going any further.
 
R

Robert Zurer

This is basically a nasty situation to be in - one thing you *could* do
instead would be to have a flag within the object which said it was
basically no longer useful, and make sure that everything you do with
the object checks that flag before going any further.

Thanks for your input.

The solution I am planning to implement, (which I was trying to avoid)
is this. Any architectual or performance-related comments or suggestions
would be appreciated.

Assuming the following to be true:

1 - All business object have a globally unique object id (OID)
2 - All business object descend from ContextBoundObject
3 - All business objects are decorated with a ContextAttribute which
will allow interception of all calls to Set or Add methods.

When a call is intercepted, an entry is made in a globally accessable
Singleton structure which will associate the OID and PropertyName of the
caller with the OID of the object being set.

Robert Zurer
(e-mail address removed)
 
I

Ivan Krivyakov

Robert Zurer said:
Assuming the following to be true:

1 - All business object have a globally unique object id (OID)
2 - All business object descend from ContextBoundObject
3 - All business objects are decorated with a ContextAttribute which
will allow interception of all calls to Set or Add methods.

When a call is intercepted, an entry is made in a globally accessable
Singleton structure which will associate the OID and PropertyName of the
caller with the OID of the object being set.

I think that the problem you are trying to solve is too general.
And even if you do find all references to an employee object,
what will you do if particular reference *is* accessed after the
employee has been removed?

It looks like objects that use employees must be ready to cope
with the fact that by the time they access the employee, it
can be long removed.

Off the top of my head, I can suggest two solutions.
They can be used separately, or together.

1. Use weak references. Each employer should have one and
only one object that it reports to ("the manager"). Only this object
should have a real reference to an employee. Manager can
remove employee, if needed. All other users should
receive weak references to the employee (see WeakReference
class) and be ready that this reference can yield null at any time.

One problem with weak references is that the object does not die
immediately after all real references are gone, so weak reference can
retrieve an employee after it has been logically removed.
Thus, you probably will want to use a Removed flag in your employee
object and an intelligent weak reference class that works along these lines:

class EmployeeWeakReference : WeakReference
{
public overrides object Target
{
get
{
object Obj = Base.Target;
if (Obj == null) return null;
Employee E = (Employee)Obj;
if (E.Removed)
return null;
return E;
}
...
}
}

2. If user objects need to be notified about employee removal,
have an OnRemove event in your employee class. Fire this
event when employee is to be removed.

Don't forget about thread safety.

If your program is multithreaded, remember that an employee can be removed
while some other thread is working with it. Either you need to serialize all
access to employees, or work out some protection mehanism that will
prevent this from happening. Note, that this problem does not go away
even if you use some sofisticated reference-tracking machinery to
find out your references.

Ivan
 
R

Robert Zurer

Thankyou for your very excellent reply.
It looks like objects that use employees must be ready to cope
with the fact that by the time they access the employee, it
can be long removed.

Yes, if there is eber a possibility of a reference being null, I always
use conditional logic in the client code.
1. Use weak references

I have not looked into WeakReferences but I will.
Thus, you probably will want to use a Removed flag in your employee

I didn't mention it, but my main aim is to have my business objects
totally agnostic of anything apart from business logic, i.e. plumbing of
the sort that is the subject of this thread. That's why I'm moving
towards using ContextBoundObject and an aspect-oriented approach.

By maintaining a "ReferenceManager" to maintain these lists, concerns
like notifying interested objects that a reference has changed or been
deleted could be handled centrally rather than in the business object
code.

Another point I didn't mention is that these objects exist in a shared
pool, accessed by all clients. Thread-safety is a big concern. Actions
like writing to an object, saving it to persistence or deleting all
references to it would have to be synchronized whereas read operations
may not have to be.

This threading business is very new to me so I would appreciate it if
any gotchas with this architecture could be brought to light.

Thanks again.


Robert Zurer
(e-mail address removed)
 
I

Ivan Krivyakov

Robert Zurer said:
I didn't mention it, but my main aim is to have my business objects
totally agnostic of anything apart from business logic, i.e. plumbing of
the sort that is the subject of this thread.

This is exactly the source of your trouble.

Deciding what to do when in the middle of your operation you discover
that employee in question has been deleted **is** the business logic.

As long as employees can be deleted, this problem will not go away.
No amount of miraculuous behind-the-scenes plumbing can help it.

If you realize that, your life will be easier :)
Thread-safety is a big concern. Actions
like writing to an object, saving it to persistence or deleting all
references to it would have to be synchronized whereas read operations
may not have to be.

Take a look at System.Threading.ReaderWriterLock.

Ivan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top