委托的定义及使用

委托应用实例
本文通过一个问候功能的例子,展示了如何利用委托来分离公共逻辑与业务逻辑,实现代码的解耦及扩展性增强。

介绍

  首先委托是对方法进行封装的一个类,它将方法作为参数来调用。

  下面来简单看看委托的三部曲,委托的声明,实例化,调用,代码如下:

 1 class DelegeteTest
 2     {
 3         public delegate void DelegateShow(); //声明
 4         public void Show()
 5         {
 6             {
 7                 //普通调用
 8                 DoSomething();
 9             }
10             {
11                 DelegateShow delegateShow = new DelegateShow(DoSomething); //实例化,方法作为参数传递
12                 delegateShow(); // 委托调用
13             }
14             //委托将方法包装成了一个对象
15         }
16 
17         /// <summary>
18         /// 普通方法
19         /// </summary>
20         public void DoSomething()
21         {
22             Console.WriteLine("this is doSomething");
23         }
24     }

  这是一个简单的委托,那么为什么要这样使用委托呢?这里我引入一个简单的例子,请看下面的代码:

 1  public class People
 2     {
 3         /// <summary>
 4         /// 对某人的简单的问候
 5         /// </summary>
 6         public void SayHI(string Name)
 7         {
 8             Console.WriteLine("你好,{0}", Name);
 9         }
10     }
11 
12   
13     {
14             People p = new People();
15             p.SayHI("张三");
16     }

这里新建一个People 类 实现一个简单的问候方法。随着程序的运行,我们的需求可能会发生变化(唉,需求变化乃万恶之源)。

 这里我们扩展一下。我们的问候对象 要区分中国人,美国人,日本人,方法就要改对应国家的语言。一般我们对原有的方法作出以下修改。

public class People
    {
        /// <summary>
        /// 对多国家的人进行问候
        /// </summary>
        public void SayHI(string Name,PeopleType peropleType)
        {
            if (peropleType == PeopleType.Chinese)
            {
                Console.WriteLine("你好,{0}", Name);
            }
            else if (peropleType == PeopleType.American)
            {
                Console.WriteLine("Hello,{0}", Name);
            }
            else if (peropleType == PeopleType.Japanese)
            {
                Console.WriteLine("&*%$%^%$^,{0}", Name);
            }
        }

        public enum PeopleType
        {
            Chinese=0,
            American=1,
            Japanese=2
           
        }
    }

//---------------------------------------------------------------------------------------
            {
                People p = new People();
                p.SayHI("张三", People.PeopleType.Chinese);
                p.SayHI("Tom", People.PeopleType.American);
                p.SayHI("漩涡鸣人", People.PeopleType.Japanese);
            }

  这样做虽然实现了功能,但是这样做不推荐,因为每增加一个分支(比如又新加了法国人,韩国人等等),都需要去修改SayHI()方法和新增一个PeopleType。这样系统耦合性太大,SayHI方法包法了所有的问候逻辑。于是我们继续升级,代码如下:

/// <summary>
        /// 对中国人的问候
        /// </summary>
        /// <param name="Name"></param>
        public void SayHIChinese(string name)
        {
            Console.WriteLine("你好,{0}", name);
        }

        /// <summary>
        /// 对美国人的问候
        /// </summary>
        /// <param name="name"></param>
        public void SayHIAmerican(string name)
        {
            Console.WriteLine("Hello,{0}", name);
        }

        /// <summary>
        /// 对日本人的问候
        /// </summary>
        /// <param name="name"></param>
        public void SayHIJapanese(string name)
        {
            Console.WriteLine("&*%$%^%$^,{0}", name);
        }


     Console.WriteLine("-----------升级---------");
            {
                People p = new People();
                p.SayHIChinese("张三丰");
                p.SayHIAmerican("Jerry");
                p.SayHIJapanese("旗木卡卡西");
            }

  这样我们把原来分支里面的拆分成上面的多个方法。这样就可以适应人物类型的扩展了,看似没有什么问题。这时我们的问题来了,我们的业务要进行扩展。我们需要在每次问候之前,结束都需要加入日志记录。代码如下:

 1 /// <summary>
 2         /// 对中国人的问候
 3         /// </summary>
 4         /// <param name="Name"></param>
 5         public void SayHIChinese(string name)
 6         {
 7             Console.WriteLine("开始问候"); //增加日志
 8             Console.WriteLine("你好,{0}", name);
 9             Console.WriteLine("问候结束");//增加日志
10         }
11 
12         /// <summary>
13         /// 对美国人的问候
14         /// </summary>
15         /// <param name="name"></param>
16         public void SayHIAmerican(string name)
17         {
18             Console.WriteLine("开始问候"); //增加日志
19             Console.WriteLine("Hello,{0}", name);
20             Console.WriteLine("问候结束");//增加日志
21         }
22 
23         /// <summary>
24         /// 对日本人的问候
25         /// </summary>
26         /// <param name="name"></param>
27         public void SayHIJapanese(string name)
28         {
29             Console.WriteLine("开始问候"); //增加日志
30             Console.WriteLine("&*%$%^%$^,{0}", name);
31             Console.WriteLine("问候结束");//增加日志
32         }

  这样做显然太麻烦,每个方法都要去添加公共逻辑(增加日志)。所以要有一个公有方法添加日志。那我们又回到最初的方法。

 1 /// <summary>
 2         /// 对多国家的人进行问候
 3         /// </summary>
 4         public void SayHI(string Name,PeopleType peropleType)
 5         {
 6             Console.WriteLine("开始问候"); //增加日志
 7             if (peropleType == PeopleType.Chinese)
 8             {
 9                 Console.WriteLine("你好,{0}", Name);
10             }
11             else if (peropleType == PeopleType.American)
12             {
13                 Console.WriteLine("Hello,{0}", Name);
14             }
15             else if (peropleType == PeopleType.Japanese)
16             {
17                 Console.WriteLine("&*%$%^%$^,{0}", Name);
18             }
19             Console.WriteLine("问候结束");//增加日志
20         }

  以上方法可以增加公有逻辑,但是是存在分支。把方法分开写,不存在分支,但是每个方法里面要增加公有逻辑,那我们有什么办法把这两种合并在一起呢。有的,引入委托解决这一问题。代码如下:

 /// <summary>
        /// 完美方法:
        ///  1,方便增加公有逻辑
        ///  2,不因逻辑而变化
        ///  直接传入逻辑(方法)
        /// </summary>
        public void SayHIPerfect(string name,SayHIDelegete sayHiDelegete)
        {
            Console.WriteLine("开始问候"); //增加日志
            sayHiDelegete.Invoke(name);
            Console.WriteLine("问候结束");//增加日志
        }
/// <summary>
        /// 对中国人的问候
        /// </summary>
        /// <param name="Name"></param>
        public void SayHIChinese(string name)
        {
            Console.WriteLine("你好,{0}", name);
        }

        /// <summary>
        /// 对美国人的问候
        /// </summary>
        /// <param name="name"></param>
        public void SayHIAmerican(string name)
        {
            Console.WriteLine("Hello,{0}", name);
        }

        /// <summary>
        /// 对日本人的问候
        /// </summary>
        /// <param name="name"></param>
        public void SayHIJapanese(string name)
        {
            Console.WriteLine("&*%$%^%$^,{0}", name);
        }


 Console.WriteLine("-----------完美版本---------");
            {
                People p = new People();
                {
                    SayHIDelegete method = new SayHIDelegete(p.SayHIChinese);
                    p.SayHIPerfect("林青霞",method);
                }
                {
                    SayHIDelegete method = new SayHIDelegete(p.SayHIAmerican);
                    p.SayHIPerfect("Lilei", method);
                }
                {
                    SayHIDelegete method = new SayHIDelegete(p.SayHIJapanese);
                    p.SayHIPerfect("山本一夫", method);
                }
            }

总结

   委托的作用

            1.解藕,将系统 里面的公共逻辑和业务逻辑分开。

           2.除本文中讲到的解藕,委托还用于事件,观察者模式,lamada表达式,异步多线程等等,可以说委托在我们的编程中无处不在。

            

 

转载于:https://www.cnblogs.com/yancyfu/p/6862773.html

<think>我们正在讨论C#中的委托(Delegate)。根据用户的问题,我们需要解释委托定义、调用方式以及在实际开发中的使用方法。同时,我们需要参考提供的引用内容。 委托定义: 根据引用[3],委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。我们可以使用`delegate`关键字来声明委托。 例如: ```csharp public delegate void MyDelegate(string message); ``` 委托的创建与调用: 创建委托实例时,需要绑定到一个具体的方法(该方法必须具有与委托相同的签名)。然后,我们可以通过委托实例来调用方法。 例如: ```csharp MyDelegate myDelegate = new MyDelegate(DisplayMessage); myDelegate("Hello, World!"); // 直接调用,等同于myDelegate.Invoke("Hello, World!"); ``` 调用方式: 根据引用[4],委托提供了多种调用方式: 1. `Invoke`:同步调用,即在当前线程上直接执行方法。 2. `BeginInvoke`:异步调用,该方法会在线程池中的线程上执行,并立即返回一个`IAsyncResult`对象,用于监控调用状态。 3. `EndInvoke`:用于获取异步调用的结果,必须与`BeginInvoke`配对使用。 4. `DynamicInvoke`:动态调用,可以接受任意参数,但类型安全在运行时检查。 多播委托: 根据引用[5],委托可以绑定多个方法,称为多播委托使用`+=`运算符添加方法,使用`-=`运算符移除方法。当调用多播委托时,会按照添加顺序依次调用所有方法。 实际开发中的使用场景: 1. 事件处理:事件基于委托模型,允许将事件处理程序绑定到事件。 2. 回调方法:例如,在异步操作完成后执行回调方法。 3. 多播委托:一个委托调用多个方法,例如在消息广播中同时通知多个对象。 下面我们按照要求组织回答:</think>### C# 委托定义、调用方式及使用场景 #### 1. **委托定义** 委托(Delegate)是一种**类型安全的函数指针**,用于封装对方法的引用。其核心特点包括: - **类型安全**:委托在编译时检查方法签名(参数类型和返回类型),确保调用安全。 - **动态绑定**:可在运行时绑定到任意匹配签名的方法。 - **多播能力**:支持通过`+=`和`-=`运算符组合多个方法(多播委托)。 **定义语法**(引用[2][3]): ```csharp delegate 返回类型 委托名(参数列表); ``` **示例**: ```csharp // 定义委托 public delegate int MathOperation(int a, int b); ``` --- #### 2. **委托的调用方式** 委托支持多种调用模式,适用于不同场景: | **调用方式** | **特点** | **适用场景** | |--------------------|--------------------------------------------------------------------------|----------------------------------| | **`Invoke`** | 同步调用,阻塞当前线程直到方法执行完成(引用[4]) | 需立即获取结果的场景 | | **`BeginInvoke`** | 异步调用,启动新线程执行方法,立即返回`IAsyncResult`(引用[4]) | 避免阻塞UI线程的耗时操作 | | **`EndInvoke`** | 获取异步调用的结果,必须与`BeginInvoke`配对使用(引用[4]) | 异步操作结果处理 | | **`DynamicInvoke`**| 动态调用,接受任意参数(运行时检查类型安全)(引用[4]) | 动态绑定未知参数类型的方法 | | **直接调用** | 简写形式(如`myDelegate()`),等效于`Invoke`(引用[3]) | 同步调用的快捷方式 | **调用示例**: ```csharp // 定义方法 int Add(int a, int b) => a + b; int Multiply(int a, int b) => a * b; // 创建委托实例 MathOperation op = new MathOperation(Add); // 同步调用 int result = op.Invoke(3, 5); // 输出8 // 多播委托(组合多个方法) op += Multiply; op(3, 5); // 依次调用Add和Multiply,返回最后一个方法的结果(15) ``` --- #### 3. **实际开发中的使用场景** ##### (1) **事件处理(核心应用)** 委托是C#事件模型的基础。例如按钮点击事件: ```csharp button.Click += (sender, e) => MessageBox.Show("Clicked!"); ``` - **原理**:`Click`事件本质是`EventHandler`委托类型(多播委托)。 ##### (2) **回调机制(引用[1][5])** 将方法作为参数传递,实现解耦: ```csharp // 定义回调委托 public delegate void DataProcessCallback(string data); // 使用回调 void FetchData(DataProcessCallback callback) { string data = GetDataFromAPI(); callback(data); // 异步处理完成后回调 } ``` ##### (3) **多播委托(引用[5])** 同时触发多个方法,适用于消息广播: ```csharp public delegate void LogHandler(string message); LogHandler loggers = ConsoleLogger; loggers += FileLogger; // 添加多个日志处理器 loggers("System started!"); // 同时输出到控制台和文件 ``` ##### (4) **异步编程** 通过`BeginInvoke`实现非阻塞操作: ```csharp MathOperation op = Multiply; op.BeginInvoke(3, 5, result => { int res = op.EndInvoke(result); Console.WriteLine(res); // 输出15 }, null); ``` ##### (5) **策略模式** 动态替换算法逻辑: ```csharp delegate int SortStrategy(int[] data); SortStrategy strategy = QuickSort; strategy = BubbleSort; // 运行时切换排序算法 ``` --- #### 4. **注意事项** 1. **线程安全**: - UI操作需通过`Control.Invoke`切换到主线程(引用[1])。 2. **内存泄漏**: - 及时用`-=`移除不再需要的委托引用。 3. **Lambda表达式**: - 简化委托实例化:`MathOperation op = (a, b) => a + b;`。 --- ### 总结 委托是C#实现**事件驱动、回调、异步编程**的核心机制。其核心价值在于: - **解耦调用方与实现方**:通过抽象方法签名提升代码灵活性。 - **支持函数式编程范式**:方法可作为一等公民传递。 - **简化异步/事件模型**:为LINQ、Task异步等高级特性奠定基础。 > 关键引用: > 委托通过`Invoke`同步调用或`BeginInvoke`异步执行方法[^4]。 > 多播委托可同时触发多个方法,适用于消息广播场景[^5]。 > 回调机制本质是委托指向目标方法[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值