A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/dotnet/csharplang/discussions/4039 below:

Weak keyword better memory managment · dotnet/csharplang · Discussion #4039 · GitHub

Just because it hasn't been explained a lots of detail yet, it's worth highlighting that anonymous delegates / lambdas make the whole concept of weak events really rather tricky.

Let's take a very simple example:

public class C
{
    void Run()
    {
        var list = new List<object>();
        Func<bool> action = () => list.Count > 0;
    }
}

If you take a look at the generated code:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass0_0
    {
        public List<object> list;

        internal bool <Run>b__0()
        {
            return list.Count > 0;
        }
    }

    private void Run()
    {
        <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
        <>c__DisplayClass0_.list = new List<object>();
        Func<bool> func = new Func<bool>(<>c__DisplayClass0_.<Run>b__0);
    }
}

As you can see, we've created this new class <>c__DisplayClass0_0. The Target of action doesn't actually have a reference to C at all -- it's got a reference to <>c__DisplayClass0_0. This means that keeping action alive won't keep C alive.

However, action is the only thing keeping <>c__DisplayClass0_0 alive. If you turn action.Target into a weak reference, then <>c__DisplayClass0_0 is going to get garbage-collected in short order, and you won't be able to invoke action any more. This will happen non-deterministically.

Life gets even worse if you do this:

public class C
{
    private int limit = 0;
    
    void Run()
    {
        var list = new List<object>();
        Func<bool> action = () => list.Count > limit;
    }
}

Now the generated code looks like this:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass1_0
    {
        public List<object> list;

        public C <>4__this;

        internal bool <Run>b__0()
        {
            return list.Count > <>4__this.limit;
        }
    }

    private int limit = 0;

    private void Run()
    {
        <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();
        <>c__DisplayClass1_.<>4__this = this;
        <>c__DisplayClass1_.list = new List<object>();
        Func<bool> func = new Func<bool>(<>c__DisplayClass1_.<Run>b__0);
    }
}

This time, action has a reference to <>c__DisplayClass1_0, and <>c__DisplayClass1_0 has a strong reference to C.

So what we want is not for action to have a weak reference to <>c__DisplayClass1_0: we need to somehow tie the lifetime of <>c__DisplayClass1_0 to the lifetime of C, so that action.Target has a strong reference to <>c__DisplayClass1_0, but we clear that reference when C is collected.

Of course, it gets even harder:

public class C
{
    private int limit = 0;
    
    void Run(object o)
    {
        Func<bool> action = () => ((List<object>)o).Count > limit;
    }
}

And the generated code:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass1_0
    {
        public object o;

        public C <>4__this;

        internal bool <Run>b__0()
        {
            return ((List<object>)o).Count > <>4__this.limit;
        }
    }

    private int limit = 0;

    private void Run(object o)
    {
        <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();
        <>c__DisplayClass1_.o = o;
        <>c__DisplayClass1_.<>4__this = this;
        Func<bool> func = new Func<bool>(<>c__DisplayClass1_.<Run>b__0);
    }
}

Now we need to tie the lifetime of <>c__DisplayClass1_0 to both C and the object o. How does that work? Do we somehow designate one of them as the important one, and keep the other alive until that one is collected? Or do we clear action.Target when either C or o is collected?

The upshot is that we have the following options:

  1. Don't allow closures (specifically, any delegate which closes over anything from its parent's scope) in weak events. This is what some weak event libraries try to do, but annoyingly WPF's WeakEventManager does not.
  2. Come up with a much more clever way of tying the lifetime of the generated <>c__DisplayClass1_0 to some other object(s).

I would love some sort of weak event support in C#, but it's a very hard problem.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4