According to Jon Skeet, "You can only call BeginInvoke on a delegate which has a single target invocation."
Why is that? What's the real reason?
Note: For clarification (and 开发者_StackOverflow中文版because I made this mistake), I am talking about the BeginInvoke
on delegates, not on controls.
I think Jon Skeet does a good job explaining in the post you linked:
How do you want the threading to work? Do you have to run each invocation synchronously, but run the whole thing asynchronously with respect to the calling thread, or could you run each invocation asynchronously?
If it's the former, just run a single threadpool work item which calls the delegate synchronously. If it's the latter, get the invocation list with Delegate.GetInvocationList and call BeginInvoke on element of the list in turn.
Basically calling BeginInvoke
on a MulticastDelegate
is ambiguous, do you want the delegates to wait for each other or not? While in theory it could decide for you, the choice has been made to force you to explicitly select the method you want by calling the delegates in a different fashion.
In other words it is a design choice to avoid confusion. Also it is important to note that BeginInvoke
has fallen out of favor and newer methods of asynchronous programming are available making updating this old standard unlikely, so even if they wanted to change now, there is no reason to.
Given any delegate type, one may fairly easily write a class which will combine delegates which are of either that type or a derived type and yield a combined delegate which will do just about everything a MulticastDelegate
can do, and a number of things it can't. The only thing that style of combined delegate would not be able to do is have its subcomponents taken out by Delegate.Remove
(since that function would just regard the combined delegate as a unit). Unlike MulticastDelegate, the combined delegate would be able to include derived delegate types, and would work just fine in places that only want one target.
In vb.net, the code would be something like:
Class DoubleAction(Of T) Private _Act1, _Act2 As Action(Of T) Private Sub New(ByVal Act1 As Action(Of T), ByVal Act2 As Action(Of T)) _Act1 = Act1 _Act2 = Act2 End Sub Private Sub Invoke(ByVal Param As T) _Act1(Param) _Act2(Param) End Sub Function Combine(ByVal Act1 As Action(Of T), ByVal Act2 As Action(Of T)) As Action(Of T) Dim newAct As New DoubleAction(Of T)(Act1, Act2) Return AddressOf newAct.Invoke End Function End Class
Translation to C# should be straightforward.
The only real problem with this approach for combining delegates is that boilerplate code is required for every generic family of delegate to be supported (since .net does not allow delegates to be used as generic type parameters).
There is also a workaround to call BeginInvoke method on a System.MulticastDelegate object:
public class Program{
public delegate void SayHello();
public void SayHelloAndWait(){
Console.WriteLine("HELLO..");
System.Threading.Thread.Sleep(5000);
Console.WriteLine("..WORLD!");
}
public void SayHi(){
Console.WriteLine("Hi world!");
}
public void Run(){
SayHello helloMethods;
helloMethods = SayHelloAndWait;
helloMethods += SayHi;
foreach(SayHello hello in helloMethods.GetInvocationList())
hello.BeginInvoke(null,null);
}
public static void Main(String[] args){
new Program().Run();
Console.Read();
}
}
The asynchronous methods are invoked consequently from the first to the last depending on the invocation list.
精彩评论