CLR定义的方法(如[delegate].BeginInvoke)在哪里

本文关键字:delegate BeginInvoke 在哪里 定义 方法 CLR | 更新日期: 2023-09-27 18:00:03

[EDIT,完全改写:]看起来我的问题措辞确实很糟糕,也很难得到回应。所以我希望这个完整的措辞有助于

MSDN告诉明确规定:Control.BeginInvoke()在创建控件句柄的线程上执行委托,通常这将是GUI线程。并且Dispatcher.BeginInvoke()将在创建Dispatcher对象的线程上运行。这将是我创建的任何线程。

但是对于委托"CLR自动定义BeginInvoke和EndInvoke",而这些调用在ThreadPool线程上运行。除了这种稍微令人惊讶的不同行为之外,我想知道如何找到自动实现的所有函数的规范。

例如:Intelli-sense显示我的委托有一个DynamicInvoke()。类System.Delegate{}确实有DynamicInvoke(),这可能意味着我的委托继承了它。但是Delegate{}没有BeginInvoke(()。委托{}具有我的委托没有的几个功能。另外,我的委托获得了一个GetObjectData()方法。这似乎来自ISerializable。

因此,总之,委托似乎从(1)CLR"自动"获得其方法,(2)委托{}的某个子集,可能是MulticastDelegate{},可能是(3)ISerializable在哪里可以找到代理获得的所有方法的全面规范?特别有趣的是BeginInvoke(),它的精确签名,因为前面提到的两个同名方法具有不同的签名集。

[有人在编辑中建议,"代表"就是"代表"。我敢说,事实并非如此。]

感谢

CLR定义的方法(如[delegate].BeginInvoke)在哪里

Control.Begin/End/InInvoke()和Dispatcher.Begin/End/InInvoke)方法的名称和行为与委托的Begin/Eend/InInvoke方法相同,但最好不要认为它们是相同的。最重要的区别是委托的方法是类型安全,这在Control和Dispatcher版本中是完全缺失的。运行时的行为也非常不同。

CLI规范ECMA335第II.14.6章详细阐述了管理委托的规则。最好读一下这一章,我只想简要介绍一下。

委托声明转换为从MulticastDelegate继承的类(不是CLI规范中指定的delegate)。该类总是正好有4个成员,它们的运行时实现由CLR提供:

  • 一个构造函数,它接受一个对象和一个IntPtr。对象是Delegate.Target,IntPtr是目标方法Delegate.method的地址。这些成员稍后在调用委托时使用,如果委托绑定到的方法是实例方法,Target属性将提供this引用,对于静态方法为null。Method属性确定调用哪个方法。您不直接指定这些参数,编译器会在使用新运算符或订阅带有+=运算符的事件处理程序时提供这些参数。在事件的情况下有很多语法糖,您不必显式使用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是相关的,委托类型的方法是而不是文档。它假设您从规范和委托声明中知道它们的作用和外观。

根据问题的主题,答案将是这些粗体行。MSDN可能不会更好,但它很好:)

Jeffrey Richter已经写下了你在上面的问题中所问的内容。他在MSDN杂志上有这篇文章。http://msdn.microsoft.com/en-us/magazine/cc164139.aspx本文将向您展示BeginInvoke和EndInvoke实际上是如何在中实现的(可能不是实际的,但非常接近)。NET CLR。在这篇文章中投入一些时间,在那之后,我认为现在你不需要提前阅读了 Jeffrey richter在他的著作CLR Via C#中也很好地解释了这一切

大多数UI应用程序都是单线程的。UI上的控件只能使用创建控件时使用的线程进行访问

获取此控件。Winforms中存在Invoke。它将在UI线程上自动调用您的代码。在WPF的世界里,我们没有控制权。援引在WPF中,我们使用Dispatcher而不是Control

现在是代理与代理

因此,为了深入研究一下,我写下这个答案。

MSDN上提到的委托是一个类。让我们获取以下代码(取自msdnhttp://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx)

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

正如您在这里看到的,我们有一个委托(用小"d"表示)。这是一个定义Delegate的关键字,或者用简单的词来说,它是一个用于定义变量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#有这个匿名方法,它可以用来调用一个方法,而不必实际将其写成一个方法。

所以上面的代码也可以写成

使用系统;//声明委托--定义必需的签名:delegate void SampleDelegate(字符串消息);

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代码。但在2的情况下,新方法将由编译器自动生成名称。

当涉及到BeginInvoke和EndInvoke时,它们用于异步调用方法。这是使用CLR提供的线程池来完成的。

基本上发生的是使用调用一个方法

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

这里的Delegate是您正在调用的方法。程序的线程将调用BeginInvoke方法,该方法将在内部调用CLR线程池线程上Delegate参数中指定的方法。然后程序继续运行,并返回一个实现IAsyncResult接口的对象。您可以使用此对象查询使用委托调用的任务的进度(请注意,作为3参数传递的delegate sd)。

CallMeUsingDelegate方法在(ThreadPool的)单独线程上调用。当任务完成时,ThreadPool将调用指定为2参数的回调方法。

看到这一切,你可能会想,为什么我们需要EndInvoke

这是因为如果你不调用EndInvoke,CLR线程池将保存对该操作的引用,并且你会泄漏一些内存。因此,在指定的回调方法中调用EndInvoke始终是一种很好的做法。

我希望这件事现在能澄清(不是全部),但能澄清一些想法