C#委托

在C和C++ 中,只能提取函数的地址,并作为一个参数传递它。C没有类型安全性。可以把任何函数传递给需要函数指针的方法。但是,这种直接方法不仅会导致一些关于类型安全性的问题,而且没有意识到:在进行面向对象编程时,几乎没有方法是孤立存在的,而是在调用方法前通常需要与类实例相关联。所以.NET FmmewOrk在语法上不允许使用这种直接方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托只是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。



1.声明委托

定义委托的语法如下:

delegate void MyMethodInvoker(int x);

在这个示例中,定义了一个委托MyMethodInvoker,并指定该委托的每个实例都可以包含一个方法的引用,该方法带有一个int参数,并返回void。理解委托的一个要点是它们的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。

假设要定义一个返回值为string不带参数的委托,可以编写如下代码:

delegate string MyMethodInvoker();

根据定义的可见性,和委托的作用域,可以在委托的定义上应用任意常见的访问修饰符:publicprivate、protected等:
public delegate string MyMethodInvoker();


2.使用委托

下面的代码段说明了如何使用委托。

private delegate string GetAString();
        static void Main()
        {
            int x = 40;
            GetAString firststringMethod = new GetAString(x.ToString);
            Console.WriteLine("string is (0)", firststringMethod());
        }
在这段代码中,实例化了类型为GetAString的一个委托,并对它进行了初始化,使它引用整型变量x的ToString方法。。在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。这个方法必须匹配最初定义委托时的签名。

实际上,给委托实例提供圆括号与调用委托类的Invoke方法完全相同。因为firststringMethod是委托类型的一个变量,所以C#编译器会用firststringMethod.Invoke()代替firststringMethod()。

为了减少输入量,只要需要委托实例,就可以只传送地址的名称,这称为委托推断,只要编译器可以把委托实例解析为特定的类型,这个C#特性就是有效的。所以上面的委托初始化也可简写为:

GetAString firststringMethod=x.ToString;


3.Action<T>和Func<T>委托

除了为每个参数和返回类型定义一个新委托类型之外,还可以使用Action<T>和Func<T>委托。泛型Action<T>委托表示引用一个vod返回类型的方法。因为这个委托类存在不同的变体,所以可以传递至多16种不同的参数类型。没有泛型参数的Action类可调用没有参数的方法。Action<T>调用带一个参数的方法,Action<T1,T2>调用带两个参数的方法

Func<T>可以以类似的方式使用,Func<T>允许调用带返回类型的方法。。与Action<T>类似,Func<T>也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。Func<out TResult>委托类型可以调用带返回类型且无参数的方法,Func<T,out TResult>调用带一个参数的方法。

所以之前自定义的委托GetAString也可以用Func<T>代替。

Func<string> firststringMethod=x.ToString;


4.Bubblesorter示例

下面的示例将说明委托的真正用途。我们要编写一个类Bubblesorter,它实现一个静态法Sort(),这个方法的第一个参数是一个对象数组,把该数组按照升序重新排列

要实现对任何类型的对象数组排序,就必须要实现泛型方法Sort<T>(),另外该方法还需一个比较函数,其两个参数的类型是T,返回值为bool。因此,可以跟Sort<T>()指定如下签名:

static public void Sort<T>(IList<T> array,Func<T,T,bool> comparison);

下面定义Bubblesorter类:

class Bubblesorter
    {
        static public void Sort<T>(IList<T> array, Func<T, T, bool> comparison)
        {
            bool swapped = true;
            do
            {
                swapped = false;
                for (int i = 0; i < array.Count; i++)
                {
                    if (comparison(array[i + 1], array[i]))
                    {
                        swapped = true;
                        T tmp = array[i + 1];
                        array[i + 1] = array[i];
                        array[i] = tmp;
                    }
                }
            } while (swapped);
        }
    }
为了使用 Bubblesorter类,下面再定义一个Employee类。

class Employee
    {
        public string Name { get; private set; }
        public float Salary { get; private set; }
        public Employee(string name, float salary)
        {
            Name = name;
            Salary = salary;
        }
        public override string ToString()
        {
            return string.Format("{0},{1:C}",Name,Salary);
        }
        public static bool CompareSalary(Employee e1, Employee e2)
        {
            return e1.Salary < e2.Salary;
        }
    }

注意,为了匹配Func<T,T,bool>委托的签名,在这个类必须定义CompareSalary,它的参数是两个Employee引用,并返回一个布尔值。

下面编写客户端代码,完成程序。

static void Main()
        {
            Employee[] employee ={
                                     new Employee("小王",4000),
                                     new Employee("小赵",3200),
                                     new Employee("小韩",3500),
                                     new Employee("小宋",5000)
                                };
            Bubblesorter.Sort<Employee>(employee, Employee.CompareSalary);
            foreach (Employee e in employee)
            {
                Console.WriteLine(e);
            }
        }

运行这段代码,结果如下:

5.多播委托

前面使用的每个委托都只包含一个方法调用。调用委托的次数与调用方法的次数相同。如果要调用多个方法,就需要多次显式调用这个委托。但是,委托也可以包含多个方法。这种委托称为多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void,否则,就只能得到委托调用的最后一个方法的结果。

多播委托可以识别运算符+和+=,另外多播委托还识别-和-=,以从委托中删除方法调用。

为了说明多播委托的用法,下面定义一个MathOperation类,它可以做两个整型数的相加和想减。

class MathOperation
    {
        public static void Add(int a,int b)
        {
            Console.WriteLine("{0}+{1}={2}",a,b,a+b);
        }
        public static void Sub(int a, int b)
        {
            Console.WriteLine("{0}-{1}={2}", a, b, a - b);
        }
    }

下面测试多播委托

static void Main()
        {
            Action<int, int> action = MathOperation.Add;
            action += MathOperation.Sub;
            action(5, 3);
            //从action中删除MathOperation.Sub
            action -= MathOperation.Sub;
            action(5, 3);
        }

运行程序,结果如下:

如果正在使用多播委托,就应知道对同一个委托调用方法链的顺序并未正式定义。因此应避免编写依赖于以特定顺序调用方法的代码。

通过一个委托调用多个方法还可能导致一个大问题。多播委托包含一个逐个调用的委托集合。如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止

为了说明这种情况,下面修改MathOperation类,在Add中抛出异常。

class MathOperation
    {
        public static void Add(int a,int b)
        {
            Console.WriteLine("{0}+{1}={2}",a,b,a+b);
            throw new Exception();
        }
        public static void Sub(int a, int b)
        {
            Console.WriteLine("{0}-{1}={2}", a, b, a - b);
        }
    }

然后在主函数中也稍作修改

static void Main()
        {
            Action<int, int> action = MathOperation.Add;
            action += MathOperation.Sub;
            try
            {
                action(5, 3);
            }
            catch
            {
                Console.WriteLine("抛出异常……");
            }
        }

运行结果为:

委托只调用了Add方法。因为Add方法抛出了一个异常,所以委托的迭代会停止。

在这种情况下,为了避免这个问题,应自己迭代方法列表。Delegate类定义GetInvocationList方法,它返回一个Delegate对象数组。现在可以使用这个委托调用与委托直接相关的方法,捕获异常,并继续下一次迭代。

static void Main()
        {
            Action<int, int> action = MathOperation.Add;
            action += MathOperation.Sub;
            foreach (Action<int, int> d in action.GetInvocationList())
            {
                try
                {
                    d(5, 3);
                }
                catch
                {
                    Console.WriteLine("抛出异常……");
                }
            }
            
        }

修改了代码后运行应用程序,会看到在捕获了异常后,将继续迭代下一个方法。

6.匿名方法

到目前为止,要想使委托工作,方法必须己经存在(即委托是用它将调用的方法的相同签名定义的)。但还有另外一种使用委托的方式:即通过匿名方法。匿名方法是用作委托的参数的一段代码。

用匿名方法定义委托的语法与前面的定义并没有区别。但在实例化委托时,就有区别了。下面是一个非常简单的控制台应用程序,它说明了如何使用匿名方法:

static void Main()
        {
            Action<int, int> action = delegate(int a,int b)
            {
                Console.WriteLine("{0}+{1}={2}",a,b,a+b);
            };
            action(5, 3);
        }






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# 委托(Delegate)是一种类型,它可以用于封装一个或多个方法,使其可以像其他对象一样传递、存储和调用。委托在事件处理、多线程等方面有着广泛的应用。 C# 委托的声明方式与方法类似,可以带有参数和返回值类型,例如: ```csharp public delegate int Calculate(int a, int b); ``` 上面的代码声明了一个名为 `Calculate` 的委托类型,它包含两个 `int` 类型的参数并返回一个 `int` 类型的值。接下来可以使用这个委托类型来封装一个或多个方法。 委托的使用步骤如下: 1. 声明委托类型 ```csharp public delegate void MyDelegate(string message); ``` 2. 定义委托变量 ```csharp MyDelegate myDelegate; ``` 3. 实例化委托变量 ```csharp myDelegate = new MyDelegate(MethodA); ``` 4. 调用委托 ```csharp myDelegate("Hello"); ``` 完整的示例代码如下: ```csharp using System; namespace DelegateDemo { public delegate void MyDelegate(string message); class Program { static void Main(string[] args) { MyDelegate myDelegate; myDelegate = new MyDelegate(MethodA); myDelegate += new MyDelegate(MethodB); myDelegate("Hello"); } static void MethodA(string message) { Console.WriteLine("MethodA: " + message); } static void MethodB(string message) { Console.WriteLine("MethodB: " + message); } } } ``` 上面的代码定义了一个名为 `MyDelegate` 的委托类型,包含一个 `string` 类型的参数并返回一个 `void` 类型的值。在 `Main` 方法中,首先将 `myDelegate` 委托变量实例化为 `MethodA` 方法,然后再将其实例化为 `MethodB` 方法。最终调用 `myDelegate` 委托变量时,将会依次调用 `MethodA` 和 `MethodB` 方法。 除了以上示例中的简单委托,还有多播委托、泛型委托、匿名委托等更加高级的委托用法,可以根据具体需求选择使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值