⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csharp_delegate_interview.txt

📁 C# delegate 型别是经过深思熟虑的创新型语言特性
💻 TXT
📖 第 1 页 / 共 2 页
字号:
唤起 Delegate Object
如之前所见,要唤起由 delegate 所搭载的方法,我们对 delegate 施加 call operator(圆括弧对):

testHarness.Tester();

这一句唤起了Tester property 的 get accessor;get accessor返回 theAction delegate handle。如果 theAction 在此刻并未指向一个 delegate object,那么就会有异常被抛出。从类别外部实行唤起动作的规范做法(delegate-test-and-execute,先实现代理,再测试,最后执行之)如下所示: 

if ( testHarness.Tester != null )
   testHarness.Tester();

对于 testHarness class,我们的方法只简单的封装这样的测试:

static public void run()
{
   if ( theAction != null )
      theAction();
}

关联多个 Delegate Objects
要让一个 delegate 搭载多个方法,我们主要使用 += operator 和 -= operator。例如,设想我们定义了一个 testHashtable class。在构造函数中,我们把各个关联的测试加入到 testHarness 中:

public class testHashtable
{
   public void test0();
   public void test1();
   testHashtable()
   {
      testHarness.Tester += new testHarness.Action( test0 );
      testHarness.Tester += new testHarness.Action( test1 );
   }
   // ...
}

同样,如果我们定义一个 testArrayList class,我们也在 default constructor 中加入关联的测试。可以注意到,这些方法是静态的。

public class testArrayList
{
   static public void testCapacity();
   static public void testSearch();
   static public void testSort();
   testArrayList()
   {
      testHarness.Tester += new
         testHarness.Action(testCapacity);
      testHarness.Tester += new testHarness.Action(testSearch);
      testHarness.Tester += new testHarness.Action(testSort);
   }
   // ...
}

当 testHarness.run 方法被唤起时,通常我们并不知道 testHashtable 和 testArrayList 中哪一个的方法先被唤起;这取决于它们构造函数被唤起的顺序。但我们可以知道的是,对于每个类别,其方法被唤起的顺序就是方法被加入 delegate 的顺序。

Delegate Objects 与 Garbage Collection(垃圾收集)
考察下列局部作用域中的代码段:

{
   Announce an = new Announce();
   testHarness.Tester += 
      new testHarness.Action
      ( an.announceTime );
}

当我们将一个非静态方法加入到 delegate object 中之后,该方法的地址,以及“用来唤起该方法,指向类别对象的句柄(handle)”都被存储起来。这导致该类别对象所关联的引用计数自动增加。

an 经由 new 表达式初始化之后,managed heap(受托管的堆)中的对象所关联的引用计数被初始化为1。当 an 被传给 delegate object 的构造函数之后,Announce 对象的引用计数增加到2。走出局部作用域之后,an 的生存期结束,该引用计数减回到1——delegate object还占用了一个。

好消息是,如果有一个 delegate 引用了某对象的一个方法,那么可以保证该对象会直到“delegate object 不再引用该方法”的时候才会被施以垃圾收集处理[4]。我们不用担心对象会在自己眼皮底下被贸然清理掉了。坏消息是,该对象将持续存在(译注:这可能是不必要的),直到 delegate object 不再引用其方法为止。可以使用 -= operator 从 delegate object 中移除该方法。例如下面修正版本的代码;在局部作用域中,announceTime 先被设置、执行,然后又从 delegate object 中被移除。

{
   Announce an = new Announce();
   Action act  = new testHarness.Action( an.announceTime );
   testHarness.Tester += act;
   testHarness.run();
   testHarness.Tester -= act;
}

我们对于设计 testHashtable class 的初始想法是,实现一个析构函数用以移除在构造函数中加入的测试用方法。然而,C# 中的析构函数调用机制与 C++ 中的却不大相同[5]。C# 的析构函数既不会因为对象生存期结束而跟着被唤起,也不会因为释放了对象最后一个引用句柄( reference handle)而被直接唤起。事实上,析构函数仅在垃圾收集器作垃圾收集时才被调用,而施行垃圾收集的时机一般是无法预料的,甚至可以根本就没施行垃圾收集。

C# 规定,资源去配动作被放进一个称为 Dispose 的方法中完成,用户可以直接调用该方法:

public void Dispose ()
{
   testHarness.Tester -= new testHarness.Action( test0 );
   testHarness.Tester -= new testHarness.Action( test1 );
}

如果某类别定义了一个析构函数,其通常都会唤起 Dispose。

访问底层的类别接口
让我们再回头看看先前的代码:

{
   Announce an = new Announce();
   Action act  = 
       new testHarness.Action
       ( an.announceTime );
   testHarness.Tester += act;
   testHarness.run();
   testHarness.Tester -= act;
}

另一种实现方案是,先检查 Tester 当前是否已经搭载了其它方法,如果是,则保存当前的委托列表(delegation list),将 Tester 重置为 act,然后调用 run,最后将 Tester 恢复为原来的状态。

我们可以利用底层的 Delegate 类别接口来获知 delegate 实际搭载的方法数目。例如,

if ( testHarness.Tester != null &&
     testHarnest.GetInvocationList().Length != 0 )
   {
      Action oldAct = testHarness.Tester;
      testHarness.Tester = act;
      testHarness.run();
      testHarness.Tester = oldAct;
   }   
else { ... }

GetInvocationList 返回 Delegate class objects 数组,数组的每个元素即代表该 delegate 当前搭载的一个方法。Length 是底层 Array class 的一个 property(属性)。Array class 实现了 C# 内建数组型别的语义[6]。

经由 Delegate class 的 Method property,我们可以获取被搭载方法的全部运行期信息。如果方法是非静态的,那么经由 Delegate class 的 Target property,我们更可以获取调用该方法之对象(译注:即该方法所属类别的那个对象)的全部运行期信息。在下面例子中,Delegate 的 methods(方法) 和 properties(属性)用红色表示:

If (testHarness.Tester != null )
{
   Delegate [] methods = test.Tester.GetInvocationList();
   foreach ( Delegate d in methods )
   {
      MethodInfo theFunction = d.Method;
      Type       theTarget   = d.Target.GetType();
   // 好的:现在我们可以知道 delegate 所搭载方法的全部信息
   }
}

总结
希望本文能够引起你对 C# delegate type 的兴趣。我认为 delegate type 为 C# 提供了一种创新性的“pointer to class method(类别方法之指针)”机制。或许本文还引起了你对 C# 语言以及 .NET class framework 的兴趣。

A good starting page for technical resources is <http://www.microsoft.com/net/>. An informative news group with Microsoft developer input dealing with both .NET and C# is <http://discuss.develop.com/dotnet.html>. Of course, questions or comments on C# or this article can be addressed to me at stanleyl@you-niversity.com. Finally, C# is currently in the process of standardization. On October 31, 2000, Hewlett-Packard, Intel, and Microsoft jointly submitted a proposed C# draft standard to ECMA, an international standards body (ECMA TC39/TG2). The current draft standard and other documentation can be found at <http://www.ecma.ch>.

致谢
I would like to thank Josee Lajoie and Marc Briand for their thoughtful review of an earlier draft of this article. Their feedback has made this a significantly better article. I would also like to thank Caro Segal, Shimon Cohen, and Gabi Bayer of you-niversity.com for providing a safety.NET.

注释
[1] 对于 C++ 程序员来说,有两点值得一题:(a) 需要在对象的型别名称之后放一对圆括弧作为 default constructor,以及(b) 用于数组下标的方括号要放在型别与数组名称之间。

[2] C# 中内建的数组是一种由 .NET class library 提供的 Array class 之对象。Array class 的静态方法和非静态方法都可以被 C# 内建数组对象使用。CopyTo 是 Array 的一个非静态方法。

[3] 与 Java 一样,C# 中的成员声明包括其访问级别。缺省的访问级别是 private。

[4] 类似的,C++ 标准要求,被引用的临时对象必须直到引用的生存期结束时才能够被销毁。

[5] 在内部实现中,析构函数甚至都不曾存在过。一个类别的析构函数会被转换成 virtual Finalize 方法。

[6] 在 C# 中,一个条件判别式的结果必须得到 Boolean 型别。对 Length 值的直接判别,如if(testHarness.Length),并不是合法的条件判断。整型值无法被隐式的转换为 Boolean 值。

Stanley B. Lippman is IT Program Chair with you-niversity.com, an interactive e-learning provider of technical courses on Patterns, C++, C#, Java, XML, ASP, and the .NET platform. Previously, Stan worked for over five years in Feature Animation both at the Disney and DreamWorks Animation Studios. He was the software Technical Director on the Firebird segment of Fantasia 2000. Prior to that, Stan worked for over a decade at Bell Laboratories. Stan is the author of C++ Primer, Essential C++, and Inside the C++ Object Model. He is currently at work on C# Primer for the DevelopMentor Book Series for Addison-Wesley. He may be reached at stanleyl@you-niversity.com.

译注
[译注1]  在C#中,所谓“method(方法)”,其实就是指我们平常所理解的成员函数,其字面意义与“function(函数)”非常接近。 

[译注2]  作者是就前述的那个 delegate type Action 声明而有此言。就一般而言,只要多个方法(methods)的返回型别相同并且参数也相同,就可以被同一个 delegate type 搭载。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -