委托---实质是一个类

 

委托的声明与实例化

目录

一、委托的定义

二、委托的声明

1、声明委托

2、在该类中定义方法,参数返回值与声明的委托对应

3、委托的实例化与调用

4、委托的本质

三、委托的应用场景

四、代码地址

一、委托的定义

将方法当作参数传递,就是委托。

 

二、委托的声明

委托类似于方法,可以有参数和返回值,但是没有方法体(但是委托的本质不是一个方法),需要用delegate修饰,可以在类的内部声明,也可以在类的外部声明。

 

1、声明委托

namespace Lanyp.Course.Delegate

{

    #region delegate out of class

    /// <summary>

    /// 定义在类的外部的无参数无返回值的委托

    /// </summary>

    public delegate void NoReturnNoParaOutClass();

    #endregion

 

    /// <summary>

    /// 自定义委托

    /// </summary>

    public class CoustomDelegate

    {

        #region delegate in class

        /// <summary>

        /// 无参数无返回值

        /// </summary>

        public delegate void NoReturnNoPara();

        /// <summary>

        /// 有参数无返回值

        /// </summary>

        public delegate void NoReturnHasPara(int x, int y);

        /// <summary>

        /// 无参数有返回值

        /// </summary>

        public delegate string HasReturnNoPara();

        /// <summary>

        /// 有参数有返回值

        /// </summary>

        public delegate int HasReturnHasPara(out int x, ref int y);

        #endregion

        

 

        }

}

 

2、在该类中定义方法,参数返回值与声明的委托对应

#region private methods

/// <summary>

/// 无参无返回值的函数

/// </summary>

private void NoReturnNoParaMethod()

{

    Console.WriteLine("无参数无返回值的方法");

}

 

/// <summary>

/// 有参数无返回值的函数

/// </summary>

/// <param name="x"></param>

/// <param name="y"></param>

private void NoReturnHasParaMethod(int x, int y)

{

    Console.WriteLine($"{x}+{y}={x + y}");

}

 

/// <summary>

/// 无参数有返回值的函数

/// </summary>

private string HasReturnNoParaMethod()

{

    return "无参数有返回值的方法";

}

 

/// <summary>

/// 有参数有返回值的函数

/// </summary>

/// <param name="x"></param>

/// <param name="y"></param>

/// <returns></returns>

private int HasReturnHasParaMethod(out int x, ref int y)

{

    x = 2;

    return x + y;

}

#endregion

 

 

3、委托的实例化与调用

委托的实例化

委托的实例化类似与类的实例化,如NoReturnNoPara noReturnNoPara = new NoReturnNoPara(NoReturnNoParaMethod);,但是区别在于需要将一个方法当作参数传递过去,需要注意的是,传过去的方法的参数与返回值必须与声明的委托一致。

执行委托的两种方式:

1)调用Invoke函数:noReturnNoPara.Invoke();

2)直接调用实例化对象,如noReturnNoPara();

#region public methods

/// <summary>

/// 主方法

/// </summary>

public void Show()

{

    Console.WriteLine("=========================无参无返回值的委托====================");

    {

        //1、委托可以实例化,在实例化的过程中需要将一个方法当作参数传递过去

        NoReturnNoPara noReturnNoPara = new NoReturnNoPara(NoReturnNoParaMethod);

        //2、执行委托的两种方式

        //noReturnNoPara.Invoke(); //方式一

        noReturnNoPara();//方式二

        //noReturnNoPara.BeginInvoke(null,null); //分配一个线程去执行方法,多线程中使用

        //noReturnNoPara.EndInvoke(null);//多线程中使用

    }

 

    Console.WriteLine("=========================有参数无返回值的委托====================");

    {

        //3、实例化的时候要求传递进来的方法结构与委托一致:要求参数和返回值与委托保持一致

        //NoReturnHasPara noReturnHasPara = new NoReturnHasPara(NoReturnNoParaMethod); //错误示范

        NoReturnHasPara noReturnHasPara = new NoReturnHasPara(NoReturnHasParaMethod); //正确示范

        //noReturnHasPara.Invoke(1, 3);//方式一

        noReturnHasPara(1, 3);//方式二

 

    }

 

    Console.WriteLine("=========================无参数有返回值的委托====================");

    {

        HasReturnNoPara hasReturnNoPara = new HasReturnNoPara(HasReturnNoParaMethod);

        string result = hasReturnNoPara.Invoke();

        Console.WriteLine(result);

    }

 

    Console.WriteLine("=========================有参数有返回值的委托====================");

    {

        HasReturnHasPara hasReturnHasPara = new HasReturnHasPara(HasReturnHasParaMethod);

        int x = 0;

        int y = 3;

        int result = hasReturnHasPara.Invoke(out x, ref y);

        Console.WriteLine($"{x}+{y}={x + y}");

    }

    Console.ReadKey();

}

 

 

4、委托的本质

委托的本质是一个类,继承自MulticastDelegate特殊类

 

 

三、委托的应用场景

思考:有两个学生,分别是北京人和上海人,他们的方言不同,如何实现不同地区的同学向别人打招呼?

 

方案一:利用枚举,优势在于没有冗余代码,但是缺点在于每次新增地名都需要修改枚举和方法,造成代码不稳定,耦合程度高

方案二:每次新增一个地方就新增一个方法:相对于方案一优势在于不用修改太多的代码,耦合低,稳定性更高,缺点在于冗余代码太多

/// <summary>

/// 如果需要实现不同的地区的同学打招呼,有以下两种常规方案

/// </summary>

public class SayHi

{

    #region 方案一:利用枚举,优势在于没有冗余代码,但是缺点在于每次新增地名都需要修改枚举和方法,造成代码不稳定,耦合程度高

    public enum From

    {

        Shanghai,

        Beijing

    }

 

    /// <summary>

    /// 利用枚举约束,使调用者尽量少出错

    /// </summary>

    /// <param name="name"></param>

    /// <param name="from"></param>

    public void SayAction(string name, From from)

    {

        Console.WriteLine("招招手~~~~");

        switch (from)

        {

            case From.Shanghai:

                Console.WriteLine($"{name}:侬好!");

                break;

            case From.Beijing:

                Console.WriteLine($"{name}:你好!");

                break;

            default:

                throw new Exception("From type was wrong!");

        }

    }

    #endregion

 

    #region 方案二:每次新增一个地方就新增一个方法:相对于方案一优势在于不用修改太多的代码,耦合低,稳定性更高,缺点在于冗余代码太多

    public void ShangHai(string name)

    {

        Console.WriteLine("招招手~~~~");

        Console.WriteLine($"{name}:侬好!");

    }

    

    public void Beijing(string name)

    {

        Console.WriteLine("招招手~~~~");

        Console.WriteLine($"{name}:你好!");

    }

    #endregion

}

 

 

那么有没有更好的解决方案呢?

 

终极解决方案:行为设计模式,利用委托传进来不同的方法,执行不同地区的学生的打招呼的方式

这样做的优点:

1、增加公共业务逻辑方便,只需要在公共方法内部增加即可,既减少代码的冗余,又最大程度保证程序的稳定

2、增加一个新的业务逻辑模块(新增一个地区的人打招呼),逻辑由调用者(Student)提供,可以做到逻辑解耦

/// <summary>

/// 委托

/// </summary>

/// <param name="name"></param>

public delegate void SayHiDelegate(string name);

 

/// <summary>

/// 扩展方法

/// </summary>

public static class StudentExtension

{

#region 调用者提供的业务逻辑

    public static void ShangHai(this Student student, string name)

    {

        Console.WriteLine($"{name}:侬好!");

    }

 

    public static void HongKong(this Student student, string name)

    {

        Console.WriteLine($"{name}:雷猴!");

    }

 

    public static void Beijing(this Student student, string name)

    {

        Console.WriteLine($"{name}:你好!");

    }

#endregion

 

#region 公共方法

    /// <summary>

    /// 终极解决方案:行为设计模式,利用委托传进来不同的方法,执行不同地区的学生的打招呼的方式

    /// 优势:

    ///   1、在该方法中可以增加公共业务逻辑,可以减少代码的冗余

    ///   2、增加一个新的业务逻辑模块(新增一个地区的人打招呼),逻辑由调用者提供,可以做到逻辑解耦

    /// </summary>

    /// <param name="student"></param>

    /// <param name="name"></param>

    /// <param name="sayHiDelegate"></param>

    public static void Action(this Student student, string name, SayHiDelegate sayHiDelegate)

    {

        Console.WriteLine("招招手~~~");

        sayHiDelegate.Invoke(name);

    }

    #enregion

}

 

 

调用时,只需要将各个不同地区的打招呼方式的扩展方法传递到委托中即可

 

{

    //上海人打招呼

    Console.WriteLine("======================上海人打招呼====================");

    Student student = new Student()

    {

        Name = "张三"

    };

    SayHiDelegate studentDelegate = new SayHiDelegate(student.ShangHai);

    student.Action(student.Name, studentDelegate);

}

{

    //北京人打招呼

    Console.WriteLine("======================北京人打招呼====================");

    var student = new Student()

    {

        Name = "王五"

    };

    var studentDelegate = new SayHiDelegate(student.Beijing);

    student.Action(student.Name, studentDelegate);

}

 

 

结论:

 

如果在工作中的代码耦合严重,公共逻辑与业务逻辑之间本应该分开,可以使用委托来解耦

如果代码冗余,可以使用委托来减少冗余代码。

四、代码地址

C#高级编程之委托

————————————————

版权声明:本文为CSDN博主「菜鸟爱飞不飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_39305029/article/details/107992992

 

Invoke

 

多播委托

 

调用委托

1.委托的出现,本质是因为把函数作为参数的设想。

 

委托(delegate)是一种引用类型,在其他语言中,与委托最接近的是函数指针,但委托不仅存储对方法入口点的引用,还存储对用于调用方法的对象实例的引用。

简单的讲委托(delegate)是一种类型安全的函数指针,首先,看下面的示例程序,在C++中使用函数指针。

 

 

上面两个函数的特点是:函数的返回值类型及参数列表都一样。那么,我们可以使用函数指针来指代这两个函数,并且可以将具体的指代过程交给用户,这样,可以减少用户判断的次数。

 

2. 函数的参数和返回值是声明规则的重点

 

3. 编译器、运行时支持这种调用才行

生成委托

编译器动态 ---生成1个方法的实例、生成1个匿名类。

 

 

对比

生成:

其实想想,你有一个工厂抽象、有N种实现

 

如果不带有属性的话,每个类都是对1个方法--相同参数、相同返回值 的不同内部实现。

 

那有N个类文件。

 

如果用委托,那意思是一样的,1个委托方法规则定义,N个委托实例--匿名类定义。

 

 

最终在系统中,都是N个类文件。

 

只是减少了生成时,自己要各种编码的费劲。

 

 

调用:

调用时,invoke,实质和调用1个静态类和静态方法,有什么区别?

其实就是一个是把方法放在了类里面,创建了类对象,然后执行类对象的方法。

一个是静态方法,直接被执行。

 

同样都是实现代码,被放在使用类的外面的地方。

 

说委托可以抽象规范,那静态方法也可以提一个最高的规范定义。

 

区别在:

委托更灵活。

如果一个静态方法最高抽象,有N种实现,

你在使用类使用的时候,是不是需要工厂类方法、或者设计模式、或者直接指定某个子级实现类具体的实现方法?这样写等于,还是增强了使用类和实现方法的耦合。要提前知道。

 

委托,我就要求一个这样输入、这样输出的,具体是啥,我不管。你实现类自己选择,随便挂哪个实现类。

 

其实这种需要把具体哪个实现类、或者事件对象向外暴露,作为参数,让外部传、或者让外部+= 对事件赋值。

 

静态方法,也可以这样做啊,一个最高抽象,外部负责传入1个方法。

不用委托,没办法传入方法。

 

因为编译器规定了。传入方法的,这种引用,名字就叫做委托。

除非你静态方法,传枚举参数,这用工厂类,做case,选择具体实现。

 

那就又绕回老路了

 

关键点:就是编译器支持了,把函数的引用作为参数。 名字叫做委托。