What is the lifetime of a delegate created by a lambda in C#?

前端 未结 5 1250
忘掉有多难
忘掉有多难 2020-12-08 04:00

Lambdas are nice, as they offer brevity and locality and an extra form of encapsulation. Instead of having to write functions which are only used once you can use a lambda.<

相关标签:
5条回答
  • 2020-12-08 04:33

    I see Skeet jumped in while I was answering, so I won't belabor that point. One thing I would suggest, to better understand how you are using things, is to get familiar with reverse engineering tools and IL. Take the code sample(s) in question and reverse engineer to IL. It will give you a great amount of information on how the code is working.

    0 讨论(0)
  • 2020-12-08 04:34

    No guarantees.

    A quick demo:

    Action GetAction()
    {
        return () => Console.WriteLine("foo");
    }
    

    Call this twice, do a ReferenceEquals(a,b), and you'll get true

    Action GetAction()
    {
        var foo = "foo";
        return () => Console.WriteLine(foo);
    }
    

    Call this twice, do a ReferenceEquals(a,b), and you'll get false

    0 讨论(0)
  • 2020-12-08 04:39

    Good question. I don't have an "academic answer," more of a practical answer: I could see a compiler optimizing the binary to use the same instance, but I wouldn't ever write code that assumes it's "guaranteed" to be the same instance.

    I upvoted you at least, so hopefully someone can give you the academic answer you're looking for.

    0 讨论(0)
  • 2020-12-08 04:52

    Based on your question here and your comment to Jon's answer I think you are confusing multiple things. To make sure it is clear:

    • The method that backs the delegate for a given lambda is always the same.
    • The method that backs the delegate for "the same" lambda that appears lexically twice is permitted to be the same, but in practice is not the same in our implementation.
    • The delegate instance that is created for a given lambda might or might not always be the same, depending on how smart the compiler is about caching it.

    So if you have something like:

    for(i = 0; i < 10; ++i)
        M( ()=>{} )
    

    then every time M is called, you get the same instance of the delegate because the compiler is smart and generates

    static void MyAction() {}
    static Action DelegateCache = null;
    
    ...
    for(i = 0; i < 10; ++i)
    {
        if (C.DelegateCache == null) C.DelegateCache = new Action ( C.MyAction )
        M(C.DelegateCache);
    }
    

    If you have

    for(i = 0; i < 10; ++i)
        M( ()=>{this.Bar();} )
    

    then the compiler generates

    void MyAction() { this.Bar(); }
    ...
    for(i = 0; i < 10; ++i)
    {
        M(new Action(this.MyAction));
    }
    

    You get a new delegate every time, with the same method.

    The compiler is permitted to (but in fact does not at this time) generate

    void MyAction() { this.Bar(); }
    Action DelegateCache = null;
    ...
    for(i = 0; i < 10; ++i)
    {
        if (this.DelegateCache == null) this.DelegateCache = new Action ( this.MyAction )
        M(this.DelegateCache);
    }
    

    In that case you would always get the same delegate instance if possible, and every delegate would be backed by the same method.

    If you have

    Action a1 = ()=>{};
    Action a2 = ()=>{};
    

    Then in practice the compiler generates this as

    static void MyAction1() {}
    static void MyAction2() {}
    static Action ActionCache1 = null;
    static Action ActionCache2 = null;
    ...
    if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
    Action a1 = ActionCache1;
    if (ActionCache2 == null) ActionCache2 = new Action(MyAction2);
    Action a2 = ActionCache2;
    

    However the compiler is permitted to detect that the two lambdas are identical and generate

    static void MyAction1() {}
    static Action ActionCache1 = null;
    ...
    if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
    Action a1 = ActionCache1;
    Action a2 = ActionCache1;
    

    Is that now clear?

    0 讨论(0)
  • 2020-12-08 04:54

    It's not guaranteed either way.

    From what I remember of the current MS implementation:

    • A lambda expression which doesn't capture any variables is cached statically
    • A lambda expression which only captures "this" could be captured on a per-instance basis, but isn't
    • A lambda expression which captures a local variable can't be cached
    • Two lambda expressions which have the exact same program text aren't aliased; in some cases they could be, but working out the situations in which they can be would be very complicated
    • EDIT: As Eric points out in the comments, you also need to consider type arguments being captured for generic methods.

    EDIT: The relevant text of the C# 4 spec is in section 6.5.1:

    Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance. The term semantically identical is used here to mean that execution of the anonymous functions will, in all cases, produce the same effects given the same arguments.

    0 讨论(0)
提交回复
热议问题