Burtsev-Alexey/net-object-deep-copy

Event subscription list being copied

Closed this issue · 12 comments

Jaex commented

Is it possible to add CopyIgnore Attribute support? Like XmlIgnore or JsonIgnore.

Because i keep getting "An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll" error in this line:
var cloneObject = CloneMethod.Invoke(originalObject, null);

And i can't figure out which object causing this error. If i have CopyIgnore i can one by one add this to fields and figure out which object causing this error.

Edit: I think copying event causing this problem. So if i have CopyIgnore attribute i can use it on event to solve it.

Can you provide sample class that reproduces the problem? Maybe I can figure out what happens.

Jaex commented

Hi, unfortunately i can't give small sample to reproduce it because it is big class.

In SetDefaultSettings() function: https://code.google.com/p/sharex/source/browse/trunk/ShareX/TaskSettings.cs
this line: "TaskSettings defaultTaskSettings = Program.DefaultTaskSettings.Copy();"
But error only happens if "public List WatchFolderList = new List();" watch folder added to this list.
So problem actually happens because of WatchFolder class: https://code.google.com/p/sharex/source/browse/trunk/HelpersLib/WatchFolder.cs
I suspect event causing this maybe because my anonymous delegates have reference to TaskSettings?
https://dl.dropbox.com/u/14076298/ShareX/2013/08/N7S4rEvxen.png
But i'm not sure is reflection can access anonymous delegate objects and trying to copy them too.

Jaex commented

I moved event out of class for solve problem. This makes my codes ugly but don't have other choice.
Would be good if there is way to ignore event copying so this problem won't happen.

I'l get to this when I have time.

Can't reproduce your problem, I tried cloning objects with events, all works fine, here is example Unit test I tried

    [TestMethod]
    public void TestEventWithSubscriber()
    {
        var eventFired = false;
        var original = new WithEvent();
        original.Event+=(x) => eventFired=true;
        var copy = Clone(original);
        Assert.IsFalse(ReferenceEquals(original, copy));
        copy.FireEvent();
        Assert.IsFalse(eventFired);
        original.FireEvent();
        Assert.IsTrue(eventFired);
    }
Jaex commented

Your event anonymous method should have reference to class which event exist so it will cause stack overflow i think.

I think you problem is with subscriber, because event is a field of Delegate Type it is copied, and Delegate consist of a number of private fields with are copied to, like _target, I think it can't copy the subscriber, copying subscribers make little sense I'll add code to nullify event subscription on object clone.

Jaex commented

I will try to make example class to reproduce problem.

Interesting, I managed to reproduce your problem using the same Unit Test above, when I wait for few seconds after event is subscribed and before Clone is executed, not sure what it means and why a pause is important, very interesting!

Jaex commented

I can't manage to reproduce my problem with my test class :(

An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module.

I guess it's when _target is disposed, I run this in unit test project and if I stay too long in debugger, unit testing environment is disposed after specific timeout. In my case because I subscribe lambda it's target is closure, which is my test class, and when event is cloned copying goes too deep, I actually saw it copying internal CLR context data, not sure how it managed to get there, guess it is referenced somewhere in unit test class.

In short, I will switch off copying events subscription list, it's pretty unpredictable what is copied.

Subscribers of delegates (Events) are not copied anymore, subscription are now nullified in object clone.
Is simple way here is what was before:
If you have object with events, then subscribers of events are cloned when you clone your object.
Now the subscribers of events are no longer cloned, the cloned object contains empty list of subscribers.