Where are CLR-defined methods like [delegate].Begi

2019-01-06 14:22发布

[EDIT, Completely rephrased:] Seems like my question was poorly worded indeed, and poorly received too. So I hope that this complete rephrasing helps...

MSDN tells clearly specifies: Control.BeginInvoke() Executes a delegate on the thread that the control's handle was created on, normally this would be the GUI thread. And Dispatcher.BeginInvoke() will run on the thread where the Dispatcher object was created. This would be any thread created by me.

But for delegates "the CLR automatically defines BeginInvoke and EndInvoke" and these calls run on a ThreadPool-thread instead. Apart from this slightly surprising different behaviour I wonder how I can find the specs of all functions that automatically get implemented.

For example: Intelli-sense shows that my delegate has a DynamicInvoke(). Class System.Delegate{} does have a DynamicInvoke() which might imply that my delegate inherits it. But Delegate{} has no BeginInvoke(). And Delegate{} has several functions that my delegate has not. Also my delegate gets a GetObjectData() method. And this seems to come from ISerializable.

So in conclusion, a delegate appears gets its methods from (1) the CLR "automatically", (2) some subset of Delegate{} possibly MulticastDelegate{}, and possibly (3) ISerializble. Where can I find a comprehensive specification of all methods a delegate gets? Especially interesting is BeginInvoke(), and it's exact signature, as the two aforementioned methods with that name have different sets of signatures.

[Someone suggested in an edit that a "delegate" is a "Delegate". I daresay, it is not.]

Thanks

2条回答
太酷不给撩
2楼-- · 2019-01-06 14:38

As per the subject of your question the answer would be these bold lines. MSDN might not be better but it is good though :)

Jeffrey Richter has written about what you asked in your question above. He has this article on MSDN magazine. http://msdn.microsoft.com/en-us/magazine/cc164139.aspx This article will show you an implementation of how actually (may be not actually but very close to) this BeginInvoke and EndInvoke actually is implemented in the .NET CLR. Invest some time in this article and after that I don't think now you need to read ahead. Jeffrey richter also has explained this all very well in his book CLR Via C#.

Most UI applications are single threaded. The controls on the UI can only be accessed using the thread with which they were created.

To acheive this Control.Invoke exists in Winforms. It will automatically invoke your code on the UI thread. In the WPF world, we don't have Control.Invoke. In WPF, we have the Dispatcher instead of Control.

Now delegate vs Delegate. Hans Passant has provided with a very nice answer.

So to dwell a little into it, I am writing this answer.

Delegate as mentioned on MSDN is a class. Lets take this code (taken from msdn http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx )

public delegate int PerformCalculation(int x, int y);

As you can see here we have delegate (note with a small 'd'). This is a keyword to define a Delegate or to put it in simple words, This is a keyword to define a variable PerformCalculation that actually contains a reference to a Method.

I think you are already aware of this, but just for completeness.

Now to use this variable and invoke a method using code like this:

using System;
// Declare delegate -- defines required signature:
delegate void SampleDelegate(string message);

class TestDelegate
{
    private void CallMeUsingDelegate(string m_param)
    {
        Console.WriteLine("Called me using parameter - " + m_param);
    }

    public static void Main(string[] args)
    {
        // Here is the Code that uses the delegate defined above.
        SampleDelegate sd = new SampleDelegate(CallMeUsingDelegate);
        sd.Invoke("FromMain");
    }
}

Now to invoke a method you need to write a full Method as CallMeUsingDelegate method above. C# has this anonymous methods which can be used to invoke a method without actually having to write it as a method.

So the code above can also be written as

using System; // Declare delegate -- defines required signature: delegate void SampleDelegate(string message);

class TestDelegate
{
    public static void Main(string[] args)
    {
        // Here is the Code that uses the delegate defined above.
        SampleDelegate sd = delegate(param) {
                        Console.WriteLine("Called me using parameter - " + param);
                    };

        sd.Invoke("FromMain");
    }
}

This does the same work as the code above. But now we need to write some less code. The compiler will create identical IL code for the both the version above. But in the 2 case, the new method will have an autogenerated name by the compiler.

When it comes to BeginInvoke and EndInvoke, they are used to invoke methods asynchronously. This is done using threadpool which is available with the CLR.

Basically what happens is you call a method using

IAsyncResult ar = sd.BeginInvoke(CallMeUsingDelegate, callMeOnCompletion, sd);

Here Delegate is method that you are invoking. What will happen is that Thread of your program will call the BeginInvoke method which will internally invoke the method specified in the Delegate parameter on a CLR ThreadPool thread. Then you program continue running and returns an object that implements IAsyncResult interface. You can use this object to query about the progress of task invoked using your delegate ( note the Delegate sd passed as the 3 parameter ).

CallMeUsingDelegate method is invoked on a separate thread ( of ThreadPool ). When the task will complete, the ThreadPool will call the Callback method specified as 2 parameter.

Looking at all this you might think, why we need the EndInvoke then???

Well it is because if you don't call the EndInvoke, the CLR ThreadPool will hold a reference to this operation and you will leak some memory. So it always a good practice to call EndInvoke in the Callback method specified.

I hope this now clears (not all) but some thoughts.

查看更多
爷、活的狠高调
3楼-- · 2019-01-06 14:54

The Control.Begin/End/Invoke() and Dispatcher.Begin/End/Invoke() methods have identical names and somewhat similar behavior to a delegate's Begin/End/Invoke() methods but it is certainly best to scrap the idea that they are the same. The most important difference is that a delegate's methods are type-safe, something that's completely missing from the Control and Dispatcher versions. Runtime behavior is very different as well.

The rules that govern a delegate are spelled out in detail in the CLI spec, ECMA 335, chapter II.14.6. It is best to read the chapter, I'll just give a synopsis.

A delegate declaration is transformed into a class that inherits from MulticastDelegate (not Delegate as specified in the CLI spec). That class always has exactly 4 members, their runtime implementation is provided by the CLR:

  • a constructor that takes an object and an IntPtr. The object is the Delegate.Target, the IntPtr is the address of the target method, Delegate.Method. These members are used later when you invoke the delegate, the Target property supplies the this reference if the method to which the delegate is bound is an instance method, null for a static method. The Method property determines which method gets invoked. You don't specify these arguments directly, the compiler supplies them when you use the new operator or subscribe an event handler with the += operator. With lots of syntax sugar in the case of events, you don't have to use the new operator explicitly.

  • an Invoke() method. The arguments of the method are dynamically generated and match the delegate declaration. Calling the Invoke() method runs the delegate target method on the same thread, a synchronous call. You rarely use it in C#, you just use the syntax sugar that allows a delegate object to be invoked by just using the object name, followed by parentheses.

  • a BeginInvoke() method, provides a way to make an asynchronous call. The method quickly completes while the target method is busy executing, similar to ThreadPool.QueueUserWorkItem but with type-safe arguments. The return type is always System.IAsyncResult, used to find out when the asynchronous call is completed and supplied to the EndInvoke() method. First argument is an optional System.AsyncCallback delegate object, it's target will automatically be called when the asynchronous call is complete. Second argument is an optional object, it will be passed as-is to the callback, useful to keep track of state. Additional arguments are dynamically generated and match the delegate declaration.

  • an EndInvoke() method. It takes a single argument of type IAsyncResult, you must pass the one you got from BeginInvoke(). It completes the asynchronous call and releases resources.

Any additional methods you see on a delegate object are the ones that are inherited from the base classes, MulticastDelegate and Delegate. Like DynamicInvoke() and GetObjectData().

Asynchronous calls are the tricky ones and you rarely need to use them. They are in fact not available in .NETCore targets, like Silverlight. The delegate target method runs on an arbitrary thread-pool thread, just like Threadpool.QueueUserWorkItem() does. Any unhandled exception it might throw is captured and terminates the thread but not your program. You must call EndInvoke(), not doing so will cause a resource leak for 10 minutes. If the target method threw an exception then it will be re-raised when you call EndInvoke(). You have no control over the thread-pool thread, there is no way to cancel or abort it. The Task or Thread classes are better alternatives.

MSDN is relevant, the methods of a delegate type are not documented. It assumes you know what they do and what they look like from the specification and the delegate declaration.

查看更多
登录 后发表回答