C #: how to avoid this potential memory leak

Suppose I have a C # class that looks like this:

public class MyClass {
    public SomeObject TheObject { get; }

    public MyClass() {
        TheObject = new SomeObject();
        TheObject.MyEvent += MyEventHandler;
    }

    private void MyEventHandler() {
        // some code
    }
}

The class creates an internal object called TheObject of type SomeObject and adds an event handler to this object.

Since TheObject is publicly available, this means that any other piece of code may contain a pointer to this object; And this, in turn, will keep an object of type MyClass alive, because TheObject has a pointer to MyClass as an event handler.

So, I assume that the only way to keep this code safe from this event is to add a finalizer in MyClass:

public ~MyClass() {
    TheObject?.MyEvent -= MyEventHandler;
}

This is too bad because the finalizer is replacing objects like MyClass with the next generation of GC, but can I fix this, is this the only way to avoid this potential memory leak?

+4
3

, , , , TheObject, .

:

  • MyClass IDisposable Dispose. # using,
  • .

:

public interface ISubscription
{
    bool IsAlive { get; }
    void Fire();
}

public class Subscrition<T> : ISubscription
    where T: class
{
    public Subscrition(T target, Action<T> fire)
    {
        this.Target = new WeakReference<T>(target);
        this.FireHandler = fire;
    }
    public WeakReference<T> Target { get; }
    public Action<T> FireHandler { get; }

    public bool IsAlive => this.Target.TryGetTarget(out var t);

    public void Fire()
    {
        if (this.Target.TryGetTarget(out var target))
        {
            this.FireHandler(target);
        }
    }
}

public class WeakEvent
{
    List<ISubscription> weakHandlers = new List<ISubscription>();

    public void Register<T>(T target, Action<T> fire)
        where T:class
    {
        this.Prune();
        this.weakHandlers.Add(new Subscrition<T>(target, fire));
    }
    public void Unregister(ISubscription subscription)
    {
        this.Prune();
        this.weakHandlers.Remove(subscription);
    }
    // Prune any dead handlers.
    public void Prune()
    {
        this.weakHandlers.RemoveAll(_ => !_.IsAlive);
    }
    public void Fire()
    {
        this.Prune(); 
        this.weakHandlers.ForEach(_ => _.Fire());

    }
}

:

public class SomeObject
{
    public WeakEvent WeakMyEvent = new WeakEvent();
}

public class MyClass
{
    public SomeObject TheObject { get; }

    public MyClass()
    {
        TheObject = new SomeObject();
        TheObject.WeakMyEvent.Register(this, t => t.MyEventHandler());
    }
    private void MyEventHandler()
    {
        // some code
    }
}

+4

IDISPOSABLE, - =, , , . , . : ?

+1

Dispose, IDisposable, Dispose . , , ( Dispose), , .

The weak event pattern uses weak references from the source object to the listener, so this relationship does not stop the GC from collecting these objects. Most controls in WPF already implement a weak event pattern. One approach to using it is through the WeakEventManager generic class.

Take a look at this article to see code examples that use a weak event pattern .. p>

0
source

Source: https://habr.com/ru/post/1689624/


All Articles