[编辑,完全改写:]似乎我的问题确实措辞不当,而且收到的也很差。所以我希望这个完整的改述有助于......
MSDN告诉我们明确指出: Control.BeginInvoke()在创建控件句柄的线程上执行委托,通常这将是GUI线程。 Dispatcher.BeginInvoke()将在创建Dispatcher对象的线程上运行。这将是我创建的任何线程。
但是对于代理“CLR automatically defines BeginInvoke和EndInvoke”而这些调用在ThreadPool-thread上运行。除了这种略微令人惊讶的不同行为,我想知道如何找到自动实现的所有函数的规范。
例如:Intelli-sense显示我的委托有一个DynamicInvoke()。类System.Delegate{}确实有一个DynamicInvoke(),这可能意味着我的委托继承了它。但Delegate {}没有BeginInvoke()。而且委托{}有几个我的代表没有的功能。我的委托也得到一个GetObjectData()方法。这似乎来自ISerializable。
总而言之,委托似乎从(1)CLR“自动”获取其方法,(2)Delegate {}的某个子集可能是MulticastDelegate {},并且可能(3)ISerializble。 我在哪里可以找到委托获得的所有方法的全面规范?特别有趣的是BeginInvoke(),它是精确的签名,,因为上述两个具有该名称的方法具有不同的签名集。
[有人在编辑中建议“委托”是“代表”。我敢说,不是。]
由于
答案 0 :(得分:22)
Control.Begin / End / Invoke()和Dispatcher.Begin / End / Invoke()方法具有与委托的Begin / End / Invoke()方法相同的名称和行为,但最好废弃他们是一样的想法。最重要的区别是委托的方法是类型安全的,这在Control和Dispatcher版本中完全没有。运行时行为也非常不同。
管理代理的规则在CLI规范ECMA 335,第II.14.6章中详细说明。最好阅读本章,我只是给出一个概要。
委托声明转换为继承自MulticastDelegate的类(不是CLI规范中指定的Delegate)。该类总是有4个成员,它们的运行时实现由CLR提供:
获取对象和IntPtr的构造函数。对象是Delegate.Target,IntPtr是目标方法的地址Delegate.Method。稍后在调用委托时使用这些成员,如果委托绑定到的方法是实例方法,则Target属性提供 this 引用,静态方法为null。 Method属性确定调用哪个方法。您没有直接指定这些参数,编译器在您使用new运算符时提供它们或使用+ =运算符订阅事件处理程序。在事件的情况下有很多语法糖,您不必显式使用 new 运算符。
一个Invoke()方法。该方法的参数是动态生成的,并与委托声明匹配。调用Invoke()方法在同一个线程上运行委托目标方法,即同步调用。你很少在C#中使用它,你只需使用语法sugar,只需使用对象名称,然后用括号来调用委托对象。
BeginInvoke()方法,提供了一种进行异步调用的方法。当目标方法忙于执行时,该方法快速完成,类似于ThreadPool.QueueUserWorkItem但具有类型安全的参数。返回类型始终是System.IAsyncResult,用于找出异步调用何时完成并提供给EndInvoke()方法。第一个参数是一个可选的System.AsyncCallback委托对象,当异步调用完成时,它的目标将自动被调用。第二个参数是一个可选的对象,它将按原样传递给回调,对于跟踪状态非常有用。其他参数是动态生成的,并与委托声明匹配。
EndInvoke()方法。它需要IAsyncResult类型的单个参数,您必须传递从BeginInvoke()获得的参数。它完成异步调用并释放资源。
您在委托对象上看到的任何其他方法都是从基类(MulticastDelegate和Delegate)继承的方法。像DynamicInvoke()和GetObjectData()。
异步调用是棘手的,你很少需要使用它们。事实上,它们在.NETCore目标中不可用,例如Silverlight。委托目标方法在任意线程池线程上运行,就像Threadpool.QueueUserWorkItem()一样。它可能抛出的任何未处理的异常都会被捕获并终止线程,但不会终止你的程序。您必须调用EndInvoke(),否则将导致资源泄漏10分钟。如果目标方法抛出异常,则在调用EndInvoke()时将重新引发异常。您无法控制线程池线程,无法取消或中止它。 Task或Thread类是更好的选择。
MSDN是相关的,委托类型的方法是不记录。它假定您从规范和委托声明中了解它们的作用以及它们的外观。
答案 1 :(得分:8)
根据你提问的主题,答案就是这些粗线。 MSDN可能不会更好,但它很好:)但
杰弗里里希特写过您在上述问题中提到的内容。他在MSDN杂志上有这篇文章。 http://msdn.microsoft.com/en-us/magazine/cc164139.aspx 本文将向您展示实际(实际上可能不是非常接近)这个BeginInvoke和EndInvoke实际上是在.NET CLR中实现的实现。花一些时间在这篇文章中,之后我认为你现在不需要提前阅读。 杰弗里·里希特也在他的书“CLR Via C#”中对此进行了很好的解释。
大多数UI应用程序都是单线程的。 UI上的控件只能使用创建它们的线程进行访问。
要实现此Control.Invoke存在于Winforms中。它将自动在UI线程上调用您的代码。 在WPF世界中,我们没有Control.Invoke。在WPF中,我们有Dispatcher而不是Control。
现在委托代表。 Hans Passant提供了一个非常好的答案。
所以要稍微考虑一下,我正在写这个答案。
MSDN上提到的委托是一个类。 让我们拿这个代码(取自msdn http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx)
public delegate int PerformCalculation(int x, int y);
正如你在这里看到的那样,我们有代表(注意一个小'd')。这是一个用于定义委托或用简单的单词表示的关键字,这是一个关键字,用于定义实际包含对Method的引用的变量PerformCalculation。
我认为你已经意识到这一点,但仅仅是为了完整。
现在使用此变量并使用如下代码调用方法:
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");
}
}
现在要调用一个方法,您需要将完整的方法编写为上面的CallMeUsingDelegate方法。 C#有这个匿名方法,它可用于调用方法而无需将其作为方法编写。
所以上面的代码也可以写成
使用System; //声明委托 - 定义所需的签名: 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");
}
}
这与上面的代码完成相同的工作。但现在我们需要编写更少的代码。 编译器将为上述版本创建相同的IL代码。但在这种情况下,新方法将由编译器生成一个自动生成的名称。
当谈到BeginInvoke和EndInvoke时,它们用于异步调用方法。这是使用可用于CLR的线程池完成的。
基本上,您使用
调用方法会发生什么IAsyncResult ar = sd.BeginInvoke(CallMeUsingDelegate, callMeOnCompletion, sd);
此处Delegate是您正在调用的方法。会发生什么是程序的Thread将调用BeginInvoke方法,该方法将在内部调用CLR ThreadPool线程上Delegate参数中指定的方法。然后程序继续运行并返回一个实现IAsyncResult接口的对象。您可以使用此对象查询使用您的委托调用的任务的进度(请注意作为3参数传递的Delegate sd)。
CallMeUsingDelegate方法在一个单独的线程(ThreadPool)上调用。任务完成后,ThreadPool将调用指定为2参数的Callback方法。
看着这一切你可能会想,为什么我们需要EndInvoke呢?
好吧,因为如果你不调用EndInvoke,CLR ThreadPool将保存对此操作的引用,你将泄漏一些内存。因此,在指定的Callback方法中调用EndInvoke总是一个好习惯。
我希望现在清除(不是全部),但有些想法。