方法组转换和匿名方法

前面的文章介绍过,C# 1.0中出现委托这个核心概念,在C# 2.0中,委托得到了很大的改进。C# 2.0中委托的改进为C# 3.0中的新特性提供了铺垫,当我们了解了匿名方法后,Lambda的学习就会变得相对容易。

下面就看看C# 2.0中委托的改进。

方法组转换

在C# 1.0中,如果要创建一个委托实例,就必须同时指定委托类型和符合委托签名的方法。但是,在C# 2.0中,支持了方法组转换,也就是说我们可以从方法组到一个兼容委托类型的隐式转换。所谓"方法组"(method group),其实就是一个方法名。

看一个例子:

class Program
{
    public delegate void ReverseStringHandler(string str);

    private static void ReverseString(string str)
    {
        char[] charArray = str.ToCharArray();
        Array.Reverse(charArray);
        Console.WriteLine(charArray);
    }

    static void Main(string[] args)
    {
        //C# 1.0中创建委托实例
        ReverseStringHandler reverseStringHandler = new ReverseStringHandler(ReverseString);
        reverseStringHandler("Hello World");

        //C# 2.0中通过方法组转换创建委托实例
        reverseStringHandler = ReverseString;
        reverseStringHandler("Good morning");
    }
}

通过方法组转换,繁琐的委托实例创建得到了简化。

匿名方法

根据C# 1.0中了解到的知识,当我们创建一个委托实例的时候,我们需要找到一个跟委托类型签名一致的方法,用这个方法来实例化一个委托对象。

看看前面的字符串反转的例子,可能"ReverseString"这个方法在程序中只会使用一次,但是为了创建委托实例,这个方法必须要存在。

在C# 2.0中引入了匿名方法,所以有了更简单的方式实现上面的例子:

public delegate void ReverseStringHandler(string str);

static void Main(string[] args)
{
    ReverseStringHandler reverseStringHandler = delegate(string str)
    {
        char[] charArray = str.ToCharArray();
        Array.Reverse(charArray);
        Console.WriteLine(charArray);
    };
    reverseStringHandler("Hello World");

    Console.Read();
}

从上面可以看到,匿名方法就是通过delegate关键字以及参数列表和具体语句块的实现。

所以说,如果用来实例化委托的方法比较简单,并且这个方法在其他地方使用的频率很低时,这时候就可以考虑用匿名方法来进行简化。

匿名方法工作原理

如果你对匿名方法仍有疑惑,建议你看看上面例子的IL代码。

在Main函数的IL代码中,可以看到编译器为我们生成了一个名为"<Main>b__0"的方法,方法接受一个string类型的参数。

.method private hidebysig static
    void Main (
        string[] args
    ) cil managed
{
    ……
    IL_0009: ldftn void AnonymousMethod.Program::‘<Main>b__0‘(string)
    ……
} // end of method Program::Main

当我们查看"<Main>b__0"方法的IL代码后,可以看到这个方法就是我们在匿名方法的语句块中定义的操作。

.method private hidebysig static
    void ‘<Main>b__0‘ (
        string str
    ) cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 23 (0x17)
    .maxstack 1
    .locals init (
        [0] char[] charArray
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: callvirt instance char[] [mscorlib]System.String::ToCharArray()
    IL_0007: stloc.0
    IL_0008: ldloc.0
    IL_0009: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array)
    IL_000e: nop
    IL_000f: ldloc.0
    IL_0010: call void [mscorlib]System.Console::WriteLine(char[])
    IL_0015: nop
    IL_0016: ret
} // end of method Program::‘<Main>b__0‘

匿名方法的实现原理就是:编译器将在匿名方法所在的类,为每个匿名方法都创建了一个方法。编译器创建的这些方法只在IL代码中有效,在C#代码中是无效的,所以C#代码不能直接使用这些方法。

其实,匿名方法更常用的地方是把匿名方法当作一个参数传递给另一个方法。大家肯定都知道List有一个FindAll的方法来查找符合条件的item,这里FindAll的参数就是一个过滤条件的委托。

List<int> numList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach(int num in numList.FindAll(delegate(int n){return n>6;}))
{
    Console.WriteLine(num);
}

说到了这里,我们就看看系统帮我们定义的委托,在C#中,Action、Func和Predicate是系统定义的委托,我们可以直接使用。上面的FindAll的参数就是一个Predicate<T>的泛型委托。

Action委托

Action<T>是无返回值的泛型委托,Action委托可以支持至少0个参数,至多16个参数。

例如:

  • Action 表示无参,无返回值的委托
  • Action<int,string> 表示有传入参数int,string无返回值的委托
  • Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托

对于前面的字符串反转的例子,我们可以使用Action委托进一步简化,这样我们连"ReverseStringHandler"这个委托也省略了:

static void Main(string[] args)
{
   Action<string> reverseString = delegate(string str)
    {
        char[] charArray = str.ToCharArray();
        Array.Reverse(charArray);
        Console.WriteLine(charArray);
    };
   reverseString("Hello World");

    Console.Read();
}

Func委托

前面看到Action委托是没有返回值的,为了解决我们有时可能需要返回值的问题,系统中又出现了Func委托。

Func<TResult>是有返回值的泛型委托,其中TResult就代表返回值的类型()。Func委托可以支持至少0个参数,至多16个参数(Func<T1,T2,…,T16,TResult>)。

例如:

  • Func<int> 表示无参,返回值为int的委托
  • Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
  • Func<object,string,int> 表示传入参数为object, string 返回值为int的委托

看个简单的例子:

Func<int, int, int> addOperation = delegate(int numA, int numB) { return numA + numB; };
Console.WriteLine(addOperation(3,4));

Predicate委托

predicate 是返回bool型的泛型委托,常常结合集合类的查询使用;Predicate有且只有一个参数,返回值固定为bool。

Predicate原型:public delegate bool Predicate<T> (T obj)

  • T: 要比较的对象的类型。
  • obj: 要按照由此委托表示的方法中定义的条件进行比较的对象。
  • 返回值:如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。

在前面结合查询的例子中,我们直接把匿名方法"delegate(int n){return n>6;}"传给了FindAll方法。

其实也可以写成,

Predicate<int> checkNum = delegate(int n) { return n > 6; };
foreach (int num in numList.FindAll(checkNum)) {……}

忽略委托参数

在有些情况下,我们并不需要委托参数,那么匿名方法可以进一步省略参数列表,只需要使用一个delegate关键字,加上作为方法的操作使用的代码块。

看一个简单的代码段:

Action printer = delegate { Console.WriteLine("Hello world"); };
printer();

注意,这个"参数通配"(paremeter wildcarding)的特性并不能适用所有的情况,如果匿名方法能够转换成多个委托类型,那么我们就需要给编译器提供更多的信息

举个例子,线程的构造函数设计两个委托类型,一个有参数,一个无参数。

public delegate void ParameterizedThreadStart(object obj)
public delegate void ThreadStart()

所以,当我们通过下面的语句创建线程的时候,前两条语句没有问题,但是第三条语句会有一个错误。

因为第三条语句中的匿名方法可以转换成多个委托类型,编译器就不知道怎么处理了,所以,我们需要显示给出参数列表。

Thread t1 = new Thread(delegate() { Console.WriteLine("this is t1"); });
Thread t2 = new Thread(delegate(object o) { Console.WriteLine("this is t2"); });
Thread t3 = new Thread(delegate { Console.WriteLine("this is t3"); });

总结

本篇文章介绍了C# 2.0中委托的改进,通过方法组转换和匿名方法,可以简化程序。

同时,看到了系统定义的三个委托类型,所以有些时候我们可以不用创建自己的委托;但是要这个权衡,如果我们要经常使用一个特定类型的委托,那还是建议定义一个有意义更加明显的委托类型。

下一篇我们将一起看看匿名方法中的变量。

时间: 2024-10-12 13:13:53

方法组转换和匿名方法的相关文章

09.C#委托转换和匿名方法(五章5.1-5.4)

今天将书中看的,自己想的写出来,供大家参考,不足之处请指正.进入正题. 在C#1中开发web form常常会遇到使用事件,为每个事件创建一个事件处理方法,在将方法赋予给事件中,会使用new EventHandler(),不同的事件有各种不同的EventHandler的派生类的实例,因为我这里使用的时Console App,原理是一样的,且看 //定义一个委托 delegate void Printer(); static void Main(string[] args) { Printer p

初始化JQuery方法与(function(){})(para)匿名方法介绍

一.初始化JQuery对象 DOM加载完成时运行代码 1.$(document).ready(function(){ 全写 // 在这里写你的代码... }); 2.jQuery(function(){ // 在这里写你的代码... }); 3.$(function(){ 简写方法,$等价于jQuery // 在这里写你的代码... }); 以上三个等价,这个函数初始化为一个jQuery对象:$(obj); 都是在DOM加载完成时运行代码 二.(funtion(){})():立即执行函数:相当于

C#学习笔记三: C#2.0泛型 可控类型 匿名方法和迭代器

前言 C#1.0的委托特性使方法作为其他方法的参数来传递,而C#2.0 中提出的泛型特性则使类型可以被参数化,从而不必再为不同的类型提供特殊版本的实现方法.另外C#2.0还提出了可空类型,匿名方法和迭代器3个优美的特性. 1,泛型1.1 泛型是什么泛型的英文表述是"generic", 这个单词意为通用的.从字面意思可知,泛型代表的就是"通用类型",它可以代替任意的数据类型,使类型参数化,从而达到之实现一个方法就可以操作多种数据类型的目的.泛型是将方法实现行为与方法操

.net基础扫盲-小例子串委托、匿名方法、lamuda表达式

我把委托理解为:委托是一种方法的格式,当然他也是一种类型. 只要是方法的格式跟委托定义的格式是一样的,那么就可以把该方法附加给该委托.看以下demo 声明委托: <p style="margin:0in;font-family:微软雅黑;font-size:12.0pt"></p><pre name="code" class="csharp">public delegate void Add(int a,int

18、(番外)匿名方法+lambda表达式

概念了解: 1.什么是匿名委托(匿名方法的简单介绍.为什么要用匿名方法) 2.匿名方法的[拉姆达表达式]方法定义 3.匿名方法的调用(匿名方法的参数传递.使用过程中需要注意什么) 什么是匿名方法? 匿名方法是C#2.0引入的一个新特性,它允许开发者声明自己的函数代码而无须使用委托函数. C#为委托提供一种机制,可以为委托定义匿名方法,匿名方法没有名称,编译器会定指定一个名称,匿名方法中不能使用跳转语句跳转到该匿名方法的外部,也不能跳转到该方法的内部.也不能在匿名方法外部使用的ref和out参数.

C#中的匿名方法

C#中的匿名方法是在C#2.0引入的,它终结了C#2.0之前版本声明委托的唯一方法是使用命名方法的时代.虽然在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式.但是,匿名方法的信息同样也适用于 Lambda 表达式,可以说 Lambda 表达式就是匿名方法演变过来的,这点后续博文会讲解推演的过程.  我们可以使用匿名方法来忽略参数列表. 这意味着匿名方法可转换为具有各种签名的委托,而这对于 Lambda 表达式来说是不可能的. 学好匿名方法,才能更加深

C# 2.0 中的新增功能03 匿名方法

连载目录    [已更新最新开发文章,点击查看详细] 在 2.0 之前的 C# 版本中,声明委托的唯一方式是使用命名方法. C# 2.0 引入匿名方法,在 C# 3.0 及更高版本中,Lambda 表达式取代匿名方法作为编写内联代码的首选方式. 但是,本主题中有关匿名方法的信息也适用于 Lambda 表达式. 在有一种情况下,匿名方法提供 Lambda 表达式中没有的功能. 使用匿名方法可省略参数列表. 这意味着匿名方法可转换为具有多种签名的委托. Lambda 表达式无法实现这一点. 有关 L

从委托、匿名方法到Lambda

前面讲过委托的知识,本次由委托过渡到Lambda表达式,更易于理解. 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] intA = { 1, 3, 5, 7 }; 6 ProcArray(intA, AddOne); 7 foreach (int i in intA) 8 { 9 Console.Write(i + " "); 10 } 11 12 Console.ReadKey(); 13 } 14

十二、C# 委托与Lambda表达式(匿名方法的另一种写法)

委托与Lambda表达式 1.委托概述 2.匿名方法 3.语句Lambda 4.表达式Lambda 5.表达式树 一.委托概述 相当于C++当中的方法指针,在C#中使用delegate 委托来提供相同的功能, 它将方法作为对象封装起来,允许在"运行时"间接地绑定一个方法调用. 声明的委托相当于一种自定义的数据类型. 1.背景 冒泡排序 1 static class SimpleSort1 2 { 3 public static void BubbleSort(int[] items)