65.9K
CodeProject 正在变化。 阅读更多。
Home

C#2 匿名方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (30投票s)

2006 年 6 月 15 日

13分钟阅读

viewsIcon

116071

downloadIcon

397

本文介绍C# 2.0语言中新增的一项名为“匿名方法”的新特性。与泛型不同,此特性不涉及新的IL指令。所有的魔法都在编译器层面完成。

标题 Practical .NET2 and C#2
作者 Patrick Smacchia
出版社 ParadoxalPress
出版日期 2006年1月
ISBN 0-9766132-2-0
价格 美元 59.95 (ParadoxalPress 价格:美元 33.99)
页数 896
网站 www.PracticalDOT.NET (浏览和下载647个代码示例和样章)

以下文章摘自《Practical .NET2 and C#2》一书的第14章

目录

本文介绍C# 2.0语言中新增的一项名为匿名方法的新特性。与泛型不同,此特性不涉及新的IL指令。所有的魔法都在编译器层面完成。

C# 2 匿名方法简介

让我们开始通过一个简单的C# 1.x 版本程序来学习C# 2.0的匿名方法。该程序首先通过委托引用并调用一个方法。

Example1.cs

class Program {
   delegate void DelegateType();
   static DelegateType GetMethod(){
      return new DelegateType(MethodBody);
   }
   static void MethodBody() {
      System.Console.WriteLine("Hello");
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      delegateInstance();
      delegateInstance();
   }
}

现在,我们用C# 2.0的匿名方法重写同一个程序。

Example2.cs

class Program {
   delegate void DelegateType();
   static DelegateType GetMethod() {
      return delegate() { System.Console.WriteLine("Hello"); };
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      delegateInstance();
      delegateInstance();
   }
}

你应该注意到:

  • 在C# 2.0中,`delegate` 关键字有了一个新的用法。当C# 2.0编译器在类的主体中遇到 `delegate` 关键字时,它期望后面跟着一个匿名方法体。
  • 可以将匿名方法赋给一个委托引用。
  • 我们明白了为什么这个特性被称为匿名方法:在 `GetMethod()` 方法体中定义的 `delegate` 实例的方法是没有命名的。然而,由于它被一个委托实例引用,所以是可以调用的。

你还应该注意到,可以使用 `+=` 操作符使一个委托实例引用多个方法(无论是匿名的还是非匿名的)。

Example3.cs

using System;
class Program{
   delegate void DelegateType();
   static void Main(){
      DelegateType delegateInstance = delegate() { 
         Console.WriteLine("Hello"); };
      delegateInstance += delegate() { Console.WriteLine("Bonjour"); };
      delegateInstance();
   }
}

正如你所料,这个程序输出:

Hello
Bonjour

匿名方法可以接受参数

如下例所示,匿名方法可以接受任何类型的参数。你还可以使用 `ref` 和 `out` 关键字来调整参数的传递方式。

Example4.cs

class Program {
   delegate int DelegateType( int valTypeParam, string refTypeParam, 
                              ref int refParam, out int outParam);
   static DelegateType GetMethod() {
      return delegate( int valTypeParam , string refTypeParam,
                       ref int refParam , out int outParam     ) {
         System.Console.WriteLine( "Hello valParam:{0} refTypeParam:{1}",
                                   valTypeParam, refTypeParam);
         refParam++;
         outParam = 9;
         return valTypeParam;
      }; // End of the body of the anonymous method.
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      int refVar = 5;
      int outVar;
      int i = delegateInstance( 1, "one", ref refVar, out outVar );
      int j = delegateInstance( 2, "two", ref refVar, out outVar );
      System.Console.WriteLine( "i:{0} j:{1} refVar:{2} outVar:{3}", 
                                i, j, refVar, outVar);
   }
}

此程序输出:

Hello valParam:1 refTypeParam:one
Hello valParam:2 refTypeParam:two
i:1 j:2 refVar:7 outVar:9

正如你所见,返回类型未在匿名方法声明中定义。匿名方法的返回类型由C# v2 编译器根据它被赋值的委托的返回类型推断得出。这个类型总是已知的,因为编译器强制将任何匿名方法赋值给一个委托。

匿名方法不能被标记为特性。此限制意味着你不能在匿名方法的参数列表中使用 `param` 关键字。事实上,使用 `param` 关键字会强制编译器将相关方法标记为 `ParamArray` 特性。

Example5.cs

using System;
class Program {
   delegate void DelegateType( params int[] arr );
   static DelegateType GetMethod() {
      // Compilation error: param is not valid in this context.
      return delegate( params int[] arr ){ Console.WriteLine("Hello");};
   }
}

语法上的一个细节

可以声明一个没有签名的匿名方法,也就是说,如果你的匿名方法不接受任何参数,你不需要在 `delegate` 关键字后面写一对括号。在这种情况下,你的方法可以被赋值给任何返回 `void` 类型且不带 `out` 参数的委托实例。显然,这种匿名方法无法访问通过委托调用传递的参数。

Example6.cs

using System;
class Program{
   delegate void DelegateType(int valTypeParam, string refTypeParam, 
                              ref int refParam);
   static void Main() {
      DelegateType delegateInstance = delegate { 
         Console.WriteLine( "Hello" ); };
      int refVar = 5;
      delegateInstance( 1, "one", ref refVar );
      delegateInstance( 2, "two", ref refVar );
   }
}

匿名方法和泛型

如下例所示,匿名方法的参数可以是泛型类型。

Example7.cs

class Foo<T> {
   delegate void DelegateType( T t );
   internal void Fct( T t ) {
      DelegateType delegateInstance = delegate( T arg ){
         System.Console.WriteLine( "Hello arg:{0}" , arg.ToString() ); };
      delegateInstance( t );
   }
}
class Program {
   static void Main() {
      Foo<DOUBLE> inst = new Foo <DOUBLE>();
      inst.Fct(5.5);
   }
}

在 .NET 2.0 中,委托类型可以用泛型参数声明。匿名方法可以被赋值给这种类型的委托实例。你只需要在赋值的双方解决类型参数即可。

Example8.cs

class Program{
   delegate void DelegateType<T>( T t );
   static void Main() {
      DelegateType<double> delegateInstance = delegate( double arg ) { 
         System.Console.WriteLine( "Hello arg:{0}" , arg.ToString() ); 
      };
      delegateInstance(5.5);
   }
}

匿名方法在实际中的应用

匿名方法特别适合定义需要通过委托调用的“小型”方法。例如,你可能会使用匿名方法来编写线程的入口点过程。

Example9.cs

using System.Threading;
class Program{
   static void Main(){
      Thread thread = new Thread( delegate() {
        System.Console.WriteLine( "ManagedThreadId:{0} Hello",
                                  Thread.CurrentThread.ManagedThreadId );
      } );
      thread.Start();
      System.Console.WriteLine( "ManagedThreadId:{0} Bonjour",     
                                Thread.CurrentThread.ManagedThreadId );
   }
}

此程序显示:

ManagedThreadId:1 Bonjour
ManagedThreadId:3 Hello

这类用途的另一个经典例子是Windows 窗体控件事件回调。

Example10.cs

public class FooForm : System.Windows.Forms.Form {
   System.Windows.Forms.Button m_Button;
   public FooForm() {
      InitializeComponent();
      m_Button.Click += delegate( object sender, System.EventArgs args ) {
         System.Windows.Forms.MessageBox.Show("m_Button Clicked");
      };
   }
   void InitializeComponent()  {/*...*/}
}

看起来匿名方法就像一项微小的语言增强。现在是时候深入了解一下,看看匿名方法远比这复杂,而且用途也更广泛。

C# 2 编译器与匿名方法

简单的方法

正如你所料,当匿名方法被编译时,编译器会在相关的类中创建一个新方法。

Example11.cs

class Program {
   delegate void DelegateType();
   static void Main() {
      DelegateType delegateInstance = delegate() { 
         System.Console.WriteLine("Hello"); };
      delegateInstance();
   }
}

以下是前一个程序编译后的程序集(使用Reflector 工具查看)。

确实,自动生成了一个名为 `

b__0()` 的新的 `private` 和 `static` 方法,其中包含我们匿名方法代码。如果我们的匿名方法声明在一个实例方法中,则生成的将是一个实例方法。

我们还注意到,生成了一个名为 `<>9_CachedAnonymousMethoddelegate1` 的 `delegateType` 类型的委托字段,用于引用我们的匿名方法。

值得注意的是,所有这些生成的成员都无法通过 C# IntelliSense 查看,因为它们的名称包含一对尖括号 `< >`。这种名称对于 IL/CLR 语法是有效的,但对于 C# 语法是不正确的。

捕获的局部变量

为了保持清晰和简单,我们还没有提及匿名方法可以访问其外部方法的局部变量。让我们通过以下示例来分析这个特性。

Example12.cs

class Program {
   delegate int DelegateTypeCounter();
   static DelegateTypeCounter MakeCounter(){
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = 
         delegate { return ++counter; };
      return delegateInstanceCounter;
   }
   static void Main() {
      DelegateTypeCounter counter1 = MakeCounter();
      DelegateTypeCounter counter2 = MakeCounter();
      System.Console.WriteLine( counter1() );
      System.Console.WriteLine( counter1() );
      System.Console.WriteLine( counter2() );
      System.Console.WriteLine( counter2() );
   }
}

此程序输出:

1
2
1
2

仔细想想,这可能会让你困惑。当线程离开 `MakeCounter()` 方法后,局部变量 `counter` 似乎仍然存在。而且,似乎存在这个“幸存”局部变量的两个实例!

请注意,在 .NET 2.0 中,CLR 和 IL 语言并没有为支持匿名方法特性进行修改。这种有趣的现象一定源于编译器。这是一个很好的“语法糖”示例。让我们来分析一下程序集。

分析清楚了,因为:

  • 编译器不仅创建了一个新方法(正如我们在上一节看到的),它还完全创建了一个名为 ` c__DisplayClass1` 的新类(在此示例中)。
  • 这个类有一个名为 `b__0()` 的实例方法。这个方法包含了我们匿名方法的代码。
  • 这个类还有一个名为 `counter` 的实例字段。这个字段跟踪局部变量 `counter` 的状态。我们说局部变量 `counter` 被匿名方法捕获了。
  • 该方法实例化了 ` c__DisplayClass1` 类。此外,它还初始化了创建实例的 `counter` 字段。

请注意,`MakeCounter()` 方法没有局部变量。对于 `counter` 变量,它使用了生成的 ` c__DisplayClass1` 类实例的同一个字段。

在解释编译器为何会有这种令人惊讶的行为之前,让我们进一步深入以充分理解它的工作原理。

捕获的局部变量与代码复杂度

以下示例比预期要微妙。

Example13.cs

using System.Threading;
class Program {
   static void Main() {
      for (int i = 0; i < 5; i++)
         ThreadPool.QueueUserWorkItem( delegate { 
            System.Console.WriteLine(i); }, null);
   }
}

此程序以非确定性的方式输出,类似:

0
1
5
5
5

这个结果迫使我们推断,局部变量 `i` 被所有线程共享。执行是非确定性的,因为 `Main()` 方法和我们的闭包(或匿名方法)由多个线程同时执行。为了说清楚,这是 `Main()` 方法的反编译代码。

private static void Main(){
   bool flag1;
   Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
   class1.i = 0;
   goto Label_0030;
Label_000F:
   ThreadPool.QueueUserWorkItem(new WaitCallback(class1.<Main>b__0), null);
   class1.i++;
Label_0030:
   flag1 = class1.i < 5;
   if ( flag1 ) {
      goto Label_000F;
   }
}

请注意,打印值5的事实表明,当显示完成时,`Main()` 方法已经完成了循环的执行。以下版本的程序具有确定性的执行。

Example14.cs

using System.Threading;
class Program {
   static void Main() {
      for (int i = 0; i < 5; i++){
         int j = i;
         ThreadPool.QueueUserWorkItem(delegate { 
            System.Console.WriteLine(j); }, null);
      }
   }
}

这次,程序输出:

0
1
2
3
4

这种行为源于局部变量 `j` 在每次迭代时都被捕获。这是 `Main()` 方法的反编译代码。

private static void Main(){
   Program.<>c__DisplayClass1 class1;
   bool flag1;
   int num1 = 0;
   goto Label_0029;
Label_0004:
   class1 = new Program.<>c__DisplayClass1();
   class1.j = num1;
   ThreadPool.QueueUserWorkItem(new WaitCallback(class1.<Main>b__0), null);
   num1++;
Label_0029:
   flag1 = num1 < 5;
   if (flag1) {
      goto Label_0004;
   }
}

这揭示了用匿名方法捕获局部变量并非易事。使用此功能时应始终小心。

请注意,捕获的局部变量不再是局部变量。如果你使用不安全代码访问此类变量,你可能需要事先(使用 C# 的 `fixed` 关键字)固定它。

匿名方法访问外部方法的参数

方法的参数始终可以视为局部变量。因此,C# 2.0 允许匿名方法使用其外部方法的参数。例如:

Example15.cs

using System;
class Program {
   delegate void DelegateTypeCounter();
   static DelegateTypeCounter MakeCounter( string counterName ) {
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = delegate{
         Console.WriteLine( counterName + (++counter).ToString() );
      };
      return delegateInstanceCounter;
   }
   static void Main() {
      DelegateTypeCounter counterA = MakeCounter("Counter A:");
      DelegateTypeCounter counterB = MakeCounter("Counter B:");
      counterA();
      counterA();
      counterB();
      counterB();
   }
}

此程序输出:

Counter A:1
Counter A:2
Counter B:1
Counter B:2

然而,匿名方法无法捕获 `out` 或 `ref` 参数。一旦你意识到这种参数不能被视为局部变量,这种限制就很容易理解了。事实上,这种参数在方法执行后仍然存在。

匿名方法访问外部类的成员

匿名方法可以访问其外部类的成员。静态成员访问很容易理解,因为在应用程序域中只有一个静态字段的实例。因此,不存在“捕获”静态字段的情况。

对实例的成员访问不那么明显。为了阐明这一点,请记住,允许访问实例成员的 `this` 引用是外部实例方法的局部变量。因此,`this` 引用被匿名方法捕获。让我们分析以下示例。

Example16.cs

delegate void DelegateTypeCounter();
class CounterBuilder {
   string m_Name; // Un champ d’instance
   internal CounterBuilder( string name ) { m_Name = name; }
   internal DelegateTypeCounter BuildCounter( string counterName ) {
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = delegate {
         System.Console.Write( counterName +(++counter).ToString() );
         // On aurait pu écrire this.m_Name.
         System.Console.WriteLine(" Counter built by: " + m_Name); 
      };
      return delegateInstanceCounter;
   }
}
class Program {
   static void Main() {
      CounterBuilder cBuilder1 = new CounterBuilder( "Factory1" );
      CounterBuilder cBuilder2 = new CounterBuilder( "Factory2" );
      DelegateTypeCounter cA = cBuilder1.BuildCounter( "Counter A:" );
      DelegateTypeCounter cB = cBuilder1.BuildCounter( "Counter B:" );
      DelegateTypeCounter cC = cBuilder2.BuildCounter( "Counter C:" );
      cA();  cA ();
      cB();  cB();
      cC();  cC();
   }
}

此程序输出:

Counter A:1 Counter built by: Factory1
Counter A:2 Counter built by: Factory1
Counter B:1 Counter built by: Factory1
Counter B:2 Counter built by: Factory1
Counter C:1 Counter built by: Factory2
Counter C:2 Counter built by: Factory2

让我们反编译 `MakeCounter()` 方法以揭示 `this` 引用的捕获。

internal DelegateTypeCounter BuildCounter(string counterName){
    CounterBuilder.<>c__DisplayClass1 class1 = new 
                                 CounterBuilder.<>c__DisplayClass1();
    class1.<>4__this = this;
    class1.counterName = counterName;
    class1.counter = 0;
    return new DelegateTypeCounter(class1.<BuildCounter>b__0);
}

请注意,`this` 引用不能被结构体中的匿名方法捕获。这是编译器错误:结构体中的匿名方法无法访问 'this' 的实例成员。请考虑在匿名方法外部将 'this' 复制到局部变量,并改用该局部变量。

匿名方法与闭包

定义:闭包与词法环境

闭包是一个函数,它在运行时创建时捕获其词法环境的值。函数的词法环境是从所关心的函数可见的变量集合。

在之前的定义中,我们仔细使用了“运行时”和“从”这两个词。这表明闭包的概念指向运行时存在的东西(如对象概念)。它还表明词法环境的概念指向代码中存在的东西,即编译时(如类概念)。因此,你可以认为 C# 2.0 匿名方法的词法环境是编译器生成的类。按照同样的思路,你可以认为这种生成类的实例是一个闭包。

闭包的定义还包含了“在运行时创建函数”的概念。主流命令式语言(如 C、C++、C# 1.0、Java 或 VB.NET 1.0)不支持在运行时创建函数实例的能力。这个特性源于函数式语言(如 Haskell 或 Lisp)。因此,C# 2.0 在支持闭包方面超越了命令式语言。然而,C# 2.0 并不是第一个支持闭包的命令式语言,因为 Perl 和 Ruby 也拥有这个特性。

关于闭包的杂谈

一个函数的结果计算既取决于其参数的值,也取决于围绕其调用的上下文。你可以将此上下文视为一组背景数据。因此,函数的参数可以看作是前台数据。因此,关于函数输入数据必须是参数的决定,应该从参数对计算的相关性来考虑。

通常,在使用面向对象语言时,函数的上下文(即实例方法的上下文)是调用它的对象的当前状态。在用非面向对象命令式语言(如 C)编程时,函数的上下文是全局变量的值。在处理闭包时,上下文是闭包创建时捕获变量的值。因此,像类一样,闭包是关联行为和数据的一种方式。在面向对象的世界中,方法和数据通过 `this` 引用关联。在函数式世界中,一个函数与捕获变量的值相关联。为了说清楚:

  • 你可以将一个对象看作是一组附加到一组数据的方法。
  • 你可以将一个闭包看作是附加到一个函数的数据集。

使用闭包代替类

上一节暗示某些类型的类可以用匿名方法替换。事实上,我们在计数器实现中已经执行了这种替换。行为是计数器的增量,而状态是它的值。然而,计数器实现并未利用传递参数给匿名方法的可能性。以下示例演示了如何利用闭包对对象的状态执行参数化计算。

Example17.cs

class Program {
   delegate void DelegateMultiplier( ref int integerToMultipl);
   static DelegateMultiplier BuildMultiplier ( int multiplierParam ) {
      return delegate( ref int integerToMultiply ) {
         integerToMultiply *= multiplierParam;
      };
   }
   static void Main() {
      DelegateMultiplier multiplierBy8 = BuildMultiplier(8);
      DelegateMultiplier multiplierBy2 = BuildMultiplier(2);
      int anInteger = 3;
      multiplierBy8( ref anInteger );
      // Here, anInteger is equal to 24.
      multiplierBy2( ref anInteger );
      // Here, anInteger is equal to 48.
   }
}

这是另一个示例,演示了如何利用闭包执行参数化计算,以从对象状态中获取值。

Example18.cs

using System;
class Article {
   public Article( decimal price ) { m_Price = price; }
   private decimal m_Price;
   public decimal Price { get { return m_Price; } }
}
class Program {
   delegate decimal DelegateTaxComputer( Article article );
   static DelegateTaxComputer BuildTaxComputer( decimal tax ) {
      return delegate( Article article ) {
         return ( article.Price * (100 + tax) ) / 100;
      };
   }
   static void Main(){
      DelegateTaxComputer taxComputer19_6 = BuildTaxComputer(19.6m);
      DelegateTaxComputer taxComputer5_5 = BuildTaxComputer(5.5m);
      Article article = new Article(97);
      Console.WriteLine("Price TAX 19.6% : " + taxComputer19_6(article) );
      Console.WriteLine("Price TAX  5.5% : " + taxComputer5_5(article) );
   }
}

理解在以上两个示例中使用闭包的强大之处在于,它们避免了我们创建小型类(实际上是由编译器隐式创建的)。

委托与闭包

仔细观察,我们注意到 .NET 1.x 中在实例方法上使用的委托在概念上接近闭包的概念。事实上,这样的委托同时引用数据(对象的当前状态)和行为。存在一个约束:行为必须是定义 `this` 引用类型的类的实例方法。

在 .NET 2.0 中,这种约束得到了最小化。由于 `Delegate.CreateDelegate()` 方法的某些重载,你现在可以引用静态方法的第一参数作为委托。例如:

Example19.cs

class Program {
   delegate void DelegateType( int writeNTime );
   // This method is public to avoid problems of reflection
   // on a non-public member.
   public static void WriteLineNTimes( string s, int nTime ) {
      for( int i=0; i < nTime; i++ )
         System.Console.WriteLine( s );
   }
   static void Main() {
      DelegateType deleg = System.Delegate.CreateDelegate(
         typeof( DelegateType ),
         "Hello",                       
         typeof(Program).GetMethod( "WriteLineNTimes" )) as DelegateType;
      deleg(4);
   }
}

此程序显示:

Hello
Hello
Hello
Hello

请注意,在内部,框架和 CLR 的 2.0 版本完全重写了委托的实现。好消息是,通过委托调用方法现在效率更高。

匿名方法与函数对象

函数对象简介

System 命名空间包含四个新的委托类型,它们在操作和从集合中获取信息时特别有用。

namespace System {
   public delegate void Action<T> ( T obj ); 
   public delegate bool Predicate<T> ( T obj ); 
   public delegate U    Converter<T,U> ( T from ); 
   public delegate int  Comparison<T> ( T x, T y );
}

以下示例展示了使用这些委托实例在列表中可以执行的四种处理(请求、计算、排序和转换)。

Example20.cs

using System.Collections.Generic;
class Program {
   class Article {
      public Article(decimal price,string name){Price=price;Name=name;}
      public readonly decimal Price;
      public readonly string  Name;
   }

   static bool IsEven(int i) { return i % 2 == 0; }
   static int sum = 0;
   static void AddToSum(int i) { sum += i; }
   static int CompareArticle(Article x, Article y){
      return Comparer<DECIMAL>.Default.Compare(x.Price, y.Price);
   }
   static decimal ConvertArticle(Article article){
      return (decimal)article.Price;
   }

   static void Main(){
      // Seek out every odd integers. 
      // Implicitly uses a ‘Predicate<T>’ delegate object.
      List integers = new List();
      for(int i=1; i<=10; i++) { 
         integers.Add(i);
      }
      List even = integers.FindAll( IsEven );

      // Sum up items of the list.
      // Implicitly uses an ‘Action<T>’ delegate object.
      integers.ForEach( AddToSum );

      // Sort items of type ‘Article’.
      // Implicitly uses a ‘Comparison<T>’ delegate object.
      List
articles = new List
(); articles.Add( new Article(5,"Shoes") ); articles.Add( new Article(3,"Shirt") ); articles.Sort( CompareArticle ); // Cast items of type ‘Article’ into ‘decimal’. // Implicitly uses a ‘Converter<T,U>’ delegate object. List<DECIMAL> artPrice = articles.ConvertAll<DECIMAL>( ConvertArticle ); } }

使用过 C++ 的标准模板库STL)的读者将识别出函数对象的概念。函数对象是一个参数化的过程。函数对象在对集合的所有元素执行相同操作时特别有用。在 C++ 中,我们重载了圆括号运算符来实现函数对象。在 .NET 中,函数对象采用委托实例的形式。事实上,在前一个程序中,隐式创建的四个委托实例就是函数对象的四个例子。

使用匿名方法和函数对象查询集合

如下例所示,C# 的匿名方法在实现函数对象方面非常适用。请注意,正如第二个函数对象存储元素之和到一个整数中一样,函数对象可以封装状态。

Example21.cs

using System.Collections.Generic;
class Program {
   class Article {
      public Article(decimal price,string name){Price=price;Name=name;}
      public readonly decimal Price;
      public readonly string  Name;
   }
   static void Main(){
      // Seek out every odd integers.
      // Implicitly uses a ‘Predicate<T>’ delegate object.
      List integers = new List();
      for(int i=1; i<=10; i++) { 
         integers.Add(i);
      } 
      List even =integers.FindAll( delegate(int i){ return i%2==0; });

      // Sum up items of the list.
      // Implicitly uses an ‘Action<T>’ delegate object.
      int sum = 0;
      integers.ForEach(delegate(int i) { sum += i; });

      // Sort items of type ‘Article’.
      // Implicitly uses a ‘Comparison<T>’ delegate object.
      List
articles = new List
(); articles.Add( new Article(5,"Shoes") ); articles.Add( new Article(3,"Shirt") ); articles.Sort(delegate(Article x, Article y) { return Comparer<DECIMAL>.Default.Compare(x.Price,y.Price); } ); // Cast items of type ‘Article’ into ‘decimal’. // Implicitly uses a ‘Converter<T,U>’ delegate object. List<DECIMAL> artPrice = articles.ConvertAll<DECIMAL> ( delegate(Article article) { return (decimal)article.Price; } ); } }

列表Array 类对函数对象的支持

函数对象的使用仅限于 `List` 和 `Array` 类型的集合。事实上,只有这些集合提供了接受函数对象来处理其元素的 C# 方法。这些方法名称自明,如下表所示:

public class List<T> : ... {
   public int FindIndex(Predicate<T> match);
   public int FindIndex(int index, Predicate<T> match);
   public int FindIndex(int index, int count, Predicate<T> match);

   public int FindLastIndex(Predicate<T> match);
   public int FindLastIndex(int index, Predicate<T> match);
   public int FindLastIndex(int index, int count, Predicate<T> match);

   public List<T> FindAll(Predicate<T> match); 
   public T Find(Predicate<T> match);
   public T FindLast(Predicate match);

   public bool Exists(Predicate<T> match);
   public bool TrueForAll(Predicate<T> match); 
 
   public int RemoveAll(Predicate<T> match);
   public void ForEach(Action<T> action); 
   public void Sort(Comparison<T> comparison);
   public List<U> ConvertAll<U>(Converter<T,U> converter);
   ...
}
public class Array {
   public static int FindIndex<T>(T[] array, int startIndex, 
                                  int count, Predicate<T> match);
   public static int FindIndex<T>(T[] array, int startIndex, 
                                  Predicate<T> match);
   public static int FindIndex<T>(T[] array, Predicate<T> match);

   public static int FindLastIndex<T>(T[] array, int startIndex, 
                                      int count, Predicate<T> match);
   public static int FindLastIndex<T>(T[] array, int startIndex, 
                                      Predicate<T> match);
   public static int FindLastIndex<T>(T[] array, Predicate<T> match);

   public static T[] FindAll<T>(T[] array, Predicate<T> match);
   public static T Find<T>(T[] array, Predicate<T> match);
   public static T FindLast<T>(T[] array, Predicate<T> match);

   public static bool Exists<T>(T[] array, Predicate<T> match);
   public static bool TrueForAll<T>(T[] array, Predicate<T> match);

   public static void ForEach<T>(T[] array, Action<T> action);
   public static void Sort<T>(T[] array, System.Comparison<T> comparison);
   public static U[] ConvertAll<T, U>( T[] array, 
                                       Converter<T, U> converter);
   ...
}
C# 2.0 匿名方法 - CodeProject - 代码之家
© . All rights reserved.