zoukankan      html  css  js  c++  java
  • .NET C# 声明、实例化和使用委托以及委托在 C# 中的发展

    本文内容

    • 委托和泛型委托
      • 委托发展:C# 中委托的发展
      • 泛型委托
    • 委托
      • 声明(定义)委托
      • 实例化委托
      • 调用委托
      • 用 Lambda表达式创建和实例化委托
    • .NET 提供的委托
      • Action 委托
      • Func 委托
      • Predicate 委托
    • 参考资料
    • 修改记录

    下载 Deom

    下载更多 Demo

    委托和泛型委托


    委托实现了函数指针,这个函数指针跟 C 的函数指针不同,它是类型安全的,确保被调用的方法签名是正确的。只要方法签名跟委托签名匹配,给委托的实例可以是实例方法,或是静态方法。

    为什么要有这个东西?我们对把数据作为函数参数很熟悉,但有时,某个方法的操作不是针对数据,而是针对另一个方法。比如,线程,用线程去执行一个方法,或是代码段;再比如,事件,事件是委托的特例,等等。

    委托发展:C# 中委托的发展

    • C# 1.0 中,通过用在其他地方定义的方法显式初始化委托来创建委托的实例。
    • C# 2.0 引入了匿名方法(anonymous method)的概念,用匿名方法初始化委托,在委托中执行未命名的内联语句块。
    • C# 3.0 引入了 Lambda 表达式,与匿名方法的概念类似,但更具表现力并且更简练。匿名方法和 Lambda 表达式统称为“匿名函数”,类似闭包(Closure)特性。
    • 通常,针对 .NET Framework 3.5 及更高版本应使用 Lambda 表达式。

    下面的示例演示了从 C# 1.0 到 C# 3.0 委托创建过程的发展:

    示例1:

    View Code
    namespace MyDelegate
    {
    class Program
    {
    delegate void TestDelegate(string s);

    static void M(string s)
    {
    System.Console.WriteLine(s);
    }

    static void Main(string[] args)
    {
    // C# 1.0: 最初的委托语法,用一个方法名初始化委托.
    TestDelegate testdelA = new TestDelegate(M);

    // C# 2.0: 用内联代码初始化委托,这个内联代码成为“匿名方法”.
    // 这个方法把一个字符串作为输入参数.
    TestDelegate testDelB = delegate(string s) { System.Console.WriteLine(s); };

    // C# 3.0: 用 Lambda 表达式初始化委托.
    // Lambda 表达式也把一个字符串(x)作为输入参数.
    // 编译器可以推断 x 的数据类型.
    TestDelegate testDelC = (x) => { System.Console.WriteLine(x); };

    // 调用委托.
    testdelA("Hello. My name is M and I write lines.");
    testDelB("That's nothing. I'm anonymous and ");
    testDelC("I'm a famous author.");

    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();
    }
    }
    }

    运行结果:

    View Code
    Hello. My name is M and I write lines.
    That's nothing. I'm anonymous and
    I'm a famous author.
    Press any key to exit.

    泛型委托

    示例 1 也可改写成泛型形式。如下所示:

    示例 2:

    View Code
    namespace MyGenericDelegate
    {
    class Program
    {
    delegate void TestGenericDelegate<T>(T s);

    static void GenericM<T>(T s)
    {
    System.Console.WriteLine(s);
    }

    static void Main(string[] args)
    {
    // C# 1.0
    TestGenericDelegate<int> testGenericDelA = new TestGenericDelegate<int>(GenericM);

    // C# 2.0
    TestGenericDelegate<string> testGenericDelB = delegate(string s) { System.Console.WriteLine(s); };

    // C# 3.0
    TestGenericDelegate<double> testGenericDelC = (x) => { System.Console.WriteLine(x); };

    // 调用委托.
    testGenericDelA(123456);
    testGenericDelB("That's nothing. I'm anonymous and ");
    testGenericDelC(123.456);

    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();
    }
    }
    }

    运行结果:

    View Code
    123456
    That's nothing. I'm anonymous and
    123.456
    Press any key to exit.

    委托


    以示例 1 为例:

    • 声明(定义)委托
    delegate void TestDelegate(string s);

    每个委托描述了方法签名和返回类型等全部细节。如 TestDelegate 定义方法有一个 string 类型的参数 s,并且返回 void 类型。

    可以在任何地方定义委托,跟定义一个类类似。委托也可以有访问修饰符。

    • 实例化委托
    TestDelegate testdelA = new TestDelegate(M);

    声明委托后,必须用某个方法实例化这个委托。用方法 M 去实例化委托 testdelA

    声明(定义)和实例化委托,有点类似一个类,类也需要定义,并实例化。

    委托在语法上,总是带有一个参数的构造函数,这个参数就是委托引用的方法。也就是说,函数指针必须指向一个方法。

    • 调用委托
    testdelA("Hello. My name is M and I write lines.");

    实例化委托后,通过委托对象的名称(后面是传递给委托的参数)调用委托对象。

    委托也可以组合、移除,如下所示:

    namespace MyDelegate
    {
        delegate void D(int x);
     
        class C
        {
            public static void M1(int i)
            {
                Console.WriteLine("C.M1: " + i);
            }
            public static void M2(int i)
            {
                Console.WriteLine("C.M2: " + i);
            }
            public void M3(int i)
            {
                Console.WriteLine("C.M3: " + i);
            }
        }
    }

    用如下代码测试:

    D cd1 = new D(C.M1);
    cd1(-1);                // call M1
    D cd2 = new D(C.M2);
    cd2(-2);                // call M2
    D cd3 = cd1 + cd2;
    cd3(10);                // call M1 then M2
    cd3 += cd1;
    cd3(20);                // call M1, M2, then M1
    C c = new C();
    D cd4 = new D(c.M3);
    cd3 += cd4;
    cd3(30);                // call M1, M2, M1, then M3
    cd3 -= cd1;             // remove last M1
    cd3(40);                // call M1, M2, then M3
    cd3 -= cd4;
    cd3(50);                // call M1 then M2
    cd3 -= cd2;
    cd3(60);                // call M1
    cd3 -= cd2;                // impossible removal is benign
    cd3(60);                // call M1
    cd3 -= cd1;                // invocation list is empty so cd3 is null
    //        cd3(70);        // System.NullReferenceException thrown
    cd3 -= cd1;                // impossible removal is benign
    • 用 Lambda 表达式创建和实例化委托。
    Func<int, bool> myFunc = x => x == 5;
    bool result = myFunc(4); // returns false of course

    其中,Func<int, bool> 是.NET 提供的已封装好的委托,用于以参数形式传递的方法,必须返回值。这样,就不用显式声明定义委托。该委托输入参数为 int,返回类型为 bool

    .NET 提供的委托


    Action 委托

    该委托以参数形式传递一个执行某操作的方法,不返回值。Action 委托有如下几个重载:

    • Action 委托
    • Action<T> 委托
    • Action<T1, T2> 委托
    • Action<T1, T2, T3> 委托
    • Action<T1, T2, T3, T4> 委托

    .NET framework 4.0 提供的重载更多。可提供 16 个输入参数。

    示例 3:以 Action<T> 带一个参数的委托为例。

    View Code
    using System;

    namespace MyAction
    {
    class Program
    {
    // 声明委托
    delegate void DisplayMessage(string message);

    static void Main(string[] args)
    {
    // 用 ShowWindowsMessage,采用命名方法实例化 DisplayMessage 委托
    DisplayMessage messageTargetA = new DisplayMessage(ShowWindowsMessage);
    DisplayMessage messageTargetB = ShowWindowsMessage;
    // 用 ShowWindowsMessage,采用命名方法实例化 Action 委托
    Action<string> messageTargetC = ShowWindowsMessage;
    // 用 ShowWindowsMessage,采用匿名方法实例化 Acton 委托
    Action<string> messageTargetD = delegate(string s) { ShowWindowsMessage(s); };
    // 用 ShowWindowsMessage,采用 Lambda 表达式实例化 Acton 委托
    Action<string> messageTargetE = s => ShowWindowsMessage(s);

    messageTargetA("Hello, World!");
    messageTargetB("Hello, World!");
    messageTargetC("Hello, World!");
    messageTargetD("Hello, World!");
    messageTargetE("Hello, World!");
    System.Console.WriteLine("Press any key to exit.");
    Console.ReadKey();
    }
    private static void ShowWindowsMessage(string message)
    {
    System.Console.WriteLine(message);
    }
    }
    }

    运行结果:

    View Code
    Hello, World!
    Hello, World!
    Hello, World!
    Hello, World!
    Hello, World!
    Press any key to exit.

    该示例最简单的形式也可写成:

    View Code
    Action<string> messageTarget = s => System.Console.WriteLine(s);
    messageTarget("Hello, World!");
    System.Console.WriteLine("Press any key to exit.");
    Console.ReadKey();

    Func 委托

    该委托以参数形式传递的方法,必须返回值。Func 委托有如下几个重载:

    • Func<TResult> 委托
    • Func<T, TResult> 委托
    • Func<T1, T2, TResult> 委托
    • Func<T1, T2, T3, TResult> 委托
    • Func(<T1, T2, T3, T4, TResult> 委托

    .NET framework 4.0 提供的重载更多。可提供 16 个输入参数。

    示例 4:以 Func(T, TResult) 待一个参数的委托为例。

    View Code
    using System;

    namespace MyFunc
    {
    delegate string ConvertMethod(string inString);

    class Program
    {
    static void Main(string[] args)
    {
    // 用 UppercaseString,以命名方法实例化委托
    ConvertMethod convertMethA = UppercaseString;
    // 用 UppercaseString,以命名方法实例化 Func 委托
    Func<string, string> convertMethB = UppercaseString;
    // 以匿名方法实例化 Func 委托
    Func<string, string> convertMethC = delegate(string s) { return s.ToUpper(); };
    Func<string, string> convertMethD = delegate(string s) { return UppercaseString(s); };
    // 以 Lambda 表达式实例化 Func 委托
    Func<string, string> convertMethE = s => s.ToUpper();

    System.Console.WriteLine(convertMethA("Dakota"));
    System.Console.WriteLine(convertMethB("Dakota"));
    System.Console.WriteLine(convertMethC("Dakota"));
    System.Console.WriteLine(convertMethD("Dakota"));
    System.Console.WriteLine(convertMethE("Dakota"));

    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();
    }
    private static string UppercaseString(string inputString)
    {
    return inputString.ToUpper();
    }
    }
    }

    运行结果:

    View Code
    DAKOTA
    DAKOTA
    DAKOTA
    DAKOTA
    DAKOTA
    Press any key to exit.

    该示例最简单的形式也可写成:

    View Code
    Func<string, string> convertMeth = s => s.ToUpper();
    System.Console.WriteLine(convertMeth("Dakota"));
    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();

    Predicate 委托

    该委托定义一组条件并确定指定对象是否符合这些条件的方法。此委托由 Array 和 List<T> 类的几种方法使用,用于在集合中搜索元素。

    示例 5:演示在数组中查找第一个 X*Y>100000 的点。

    View Code
    using System;
    using System.Collections.Generic;

    namespace MyPredicate
    {
    class Program
    {
    static void Main(string[] args)
    {
    Point[] points = {
    new Point(){X = 100,Y = 200}, new Point(){X = 150,Y = 250},
    new Point(){X = 250,Y = 375}, new Point(){X = 275,Y = 395},
    new Point(){X = 295,Y = 450}, new Point(){X = 290,Y = 451}
    };

    Point first = Array.Find(points, ProductGT10);
    Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();
    }
    class Point
    {
    public int X { get; set; }
    public int Y { get; set; }
    }
    private static bool ProductGT10(Point p)
    {
    if (p.X * p.Y > 100000)
    return true;
    else
    return false;
    }
    }
    }

    也可以这些写:

    View Code
    Point[] points = { 
    new Point(){X = 100,Y = 200}, new Point(){X = 150,Y = 250},
    new Point(){X = 250,Y = 375}, new Point(){X = 275,Y = 395},
    new Point(){X = 295,Y = 450}, new Point(){X = 290,Y = 451}
    };

    Point first = Array.Find(points,
    (p) =>
    {
    if (p.X * p.Y > 100000)
    return true;
    else
    return false;
    });
    Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();

    说明:无需显示创建委托,或是指定泛型方法的参数类型,因为编译器会根据上下文自己确定。

    参考资料


    修改记录


    • 2015年1月29日 【UPDATE】

    下载 Deom

    下载更多 Demo

  • 相关阅读:
    云计算上的个人数据隐私问题 狼人:
    应用兼容性Android Studio IDEA:基于IDEA的安卓开发环境
    最小生成树卡hdu1875畅通工程再续
    编译文件编译PHP的windows版本
    动作运动预防颈椎之痛的几个小技巧
    文件资源使用Texture管理cocosBuilder项目资源:纹理文件使用(TexturePacker)
    配置级别greenplum 可用空间计算
    返回定义利用DataTable、DataSet返回SQL Server的表或者单个字段
    构造函数对象[置顶] 揭开Javascript属性constructor/prototype的底层原理
    缓存二级缓存Spring环境下Hibernate二级缓存的应用
  • 原文地址:https://www.cnblogs.com/liuning8023/p/2266258.html
Copyright © 2011-2022 走看看