.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

posted @ 2011-11-28 15:22  船长&CAP  阅读(877)  评论(0编辑  收藏  举报
免费流量统计软件