C# Lambdas and “this” variable scope

后端 未结 3 2118
悲&欢浪女
悲&欢浪女 2021-02-19 17:34

I am wondering whether I can use the this keyword inside a C# lambda, although actually I know that I can but I want to make sure that this isn\'t a bad th

相关标签:
3条回答
  • 2021-02-19 18:03

    It is absolutelly fine to use this in lambdas, but there is some stuff you should keep in mind:

    • this will be kept in memory until the lambda is not used anymore
    • if you do not pass lambda "with this" outside your class, then you will not face problems
    • if you do pass lambda "with this" outside your class, then you should remember, that your class will not be collected by the GC until there are references to the lambda left.

    And related to your use-case, you should keep in mind that the Repository instance will never be collected by the GC until people it created are in use.

    0 讨论(0)
  • 2021-02-19 18:21

    There is nothing wrong with using this in a lambda, but as you mention, if you do use this (or if you use it implicitly, by calling any nonstatic member function or using a nonstatic member variable) then the garbage collector will keep the object that this refers to alive at least as long as the delegate is alive. Since you pass a lambda to Lazy, this implies that the Repository will be alive at least as long as the Lazy object is alive (even if you never call Lazy.Value).

    To demystify it a bit, it helps to look in a disassembler. Consider this code:

    class Foo {
        static Action fLambda, gLambda;
    
        int x;
        void f() {
            int y = 0;
            fLambda = () => ++y;
        }
        void g() {
            int y = 0;
            gLambda = () => y += x;
        }
    }
    

    The standard compiler changes this to the following (try to ignore the <> extra angle brackets). As you can see, lambdas that use variables from inside the function body are transformed into classes:

    internal class Foo
    {
        private static Action fLambda;
        private static Action gLambda;
        private int x;
    
        private void f()
        {
            Foo.<>c__DisplayClass1 <>c__DisplayClass = new Foo.<>c__DisplayClass1();
            <>c__DisplayClass.y = 0;
            Foo.fLambda = new Action(<>c__DisplayClass.<f>b__0);
        }
        private void g()
        {
            Foo.<>c__DisplayClass4 <>c__DisplayClass = new Foo.<>c__DisplayClass4();
            <>c__DisplayClass.<>4__this = this;
            <>c__DisplayClass.y = 0;
            Foo.gLambda = new Action(<>c__DisplayClass.<g>b__3);
        }
    
        [CompilerGenerated]
        private sealed class <>c__DisplayClass1
        {
            public int y;
            public void <f>b__0()
            {
                this.y++;
            }
        }
        [CompilerGenerated]
        private sealed class <>c__DisplayClass4
        {
            public int y;
            public Foo <>4__this;
            public void <g>b__3()
            {
                this.y += this.<>4__this.x;
            }
        }
    
    }
    

    If you use this, whether implicitly or explicitly, it becomes a member variable in the compiler-generated class. So the class for f(), DisplayClass1, does not contain a reference to Foo, but the class for g(), DisplayClass2, does.

    The compiler handles lambdas in a simpler manner if they don't reference any local variables. So consider some slightly different code:

    public class Foo {
        static Action pLambda, qLambda;
    
        int x;
        void p() {
            int y = 0;
            pLambda = () => Console.WriteLine("Simple lambda!");
        }
        void q() {
            int y = 0;
            qLambda = () => Console.WriteLine(x);
        }
    }
    

    This time the lambdas don't reference any local variables, so the compiler translates your lambda functions into ordinary functions. The lambda in p() does not use this so it becomes a static function (called <p>b__0); the lambda in q() does use this (implicitly) so it becomes a non-static function (called <q>b__2):

    public class Foo {
        private static Action pLambda, qLambda;
    
        private int x;
        private void p()
        {
            Foo.pLambda = new Action(Foo.<p>b__0);
        }
        private void q()
        {
            Foo.qLambda = new Action(this.<q>b__2);
        }
        [CompilerGenerated] private static void <p>b__0()
        {
            Console.WriteLine("Simple lambda!");
        }
        [CompilerGenerated] private void <q>b__2()
        {
            Console.WriteLine(this.x);
        }
        // (I don't know why this is here)
        [CompilerGenerated] private static Action CS$<>9__CachedAnonymousMethodDelegate1;
    }
    

    Note: I viewed the compiler output using ILSpy with the option "decompile anonymous methods/lambdas" turned off.

    0 讨论(0)
  • 2021-02-19 18:26

    While it is correct to use this in a lambda like that, you just need to be aware that your Repository object will not be garbage collectable until your Person object is garbage collectable.

    You might want to have a field to cache the result from your lambda, and once it is Lazy filled, release the lambda since you do not need it anymore.

    Something like:

    private Lazy<string> nameProxy; 
    private string name;
    public string Name 
    { 
      get 
      {
        if(name==null)
        {
          name = nameProxy.Value;
          nameProxy = null;
        }
        return name;
      } 
    } 
    
    0 讨论(0)
提交回复
热议问题