C# compiler oddity with delegate constructors

后端 未结 3 1081
孤城傲影
孤城傲影 2020-12-31 16:31

Based on the following question, I found some odd behaviour of the c# compiler.

The following is valid C#:

static void K() {}

static void Main()
{
          


        
相关标签:
3条回答
  • 2020-12-31 16:58

    The spec (section 7.6.10.5) says:

    • The new delegate instance is initialized with the same invocation list as the delegate instance given by E.

    Now suppose the compiler translated it to something similar to your suggestion of:

    new Action( a.Target, a.Method)
    

    That would only ever create a delegate with an invocation list of a single method call. For a multi-cast delegate, it would violate the spec.

    Sample code:

    using System;
    
    class Program
    {
        static void Main(string[] args)
        {
            Action first = () => Console.WriteLine("First");
            Action second = () => Console.WriteLine("Second");
    
            Action both = first + second;
            Action wrapped1 =
                (Action) Delegate.CreateDelegate(typeof(Action),
                                                 both.Target, both.Method);
            Action wrapped2 = new Action(both);
    
            Console.WriteLine("Calling wrapped1:");
            wrapped1();
    
            Console.WriteLine("Calling wrapped2:");
            wrapped2();
        }
    }
    

    Output:

    Calling wrapped1:
    Second
    Calling wrapped2:
    First
    Second
    

    As you can see, the real behaviour of the compiler matches the spec - your suggested behaviour doesn't.

    This is partly due to the somewhat odd "sometimes single-cast, sometimes multi-cast" nature of Delegate, of course...

    0 讨论(0)
  • 2020-12-31 17:00
    • delegate is a class
    • Action delegate has a constructor like so

      public extern Action(object @object, IntPtr method);

    • Since K is a static method there is no need to pass object to inner most action instance as first argument and hence it passes null

    • Since second argument is pointer to function therefore it passes pointer of K method using ldftn function
    • for the remaining Action instances the object is passed is inner Action and the second parameter is the Invoke method since when you call a delegate you're actually calling the Invoke method

    Summary

    var action = new Action(K) => Action action = new Action(null, ldftn(K))
    new Action(action) => new Action(action, ldftn(Action.Invoke))
    

    I hope this explains what is happening?

    0 讨论(0)
  • 2020-12-31 17:03

    When you try to treat a delegate as a method, the compiler actually uses the delegate's Invoke() method. So, for example, the two lines below compile to the exact same IL (both call Invoke()):

    k();
    k.Invoke();
    

    I assume the oddity you're seeing is a consequence of this. The delegate constructor expects a method (or rather, a method group), but it gets a delegate instead. So it treats it as a method and uses the Invoke() method.

    As for the meaning, it is delegate that calls delegate that calls the actual method. You can verify this yourself by accessing the delegate's Method and Target properties. In the case of the outer-most delegate, Method is Action.Invoke and Target the inner delegate.

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