C#基础--委托

这篇具有很好参考价值的文章主要介绍了C#基础--委托。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C#基础–委托

C#基础–委托

简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名)

一、什么是委托,委托的本质是什么?

  1. 跟方法有点类似,有参数,返回值,访问修饰符+ delegate

    public delegate void NoReturnNoPara();
    
    
  2. 委托的本质是一个类,继承自一个特殊类 MulticastDelegate,我们自己在定义类的时候是无法去继承MulticastDelegate

  3. 在类的内部生成一个类

C#基础--委托,C# .Net,c#,数据库,microsoft

二、委托的实例化,执行委托

  1. ILSply反编译–委托的本质其实是一个类

  2. 委托本质是一个类,这个类的构造函数参数—Method–方法

C#基础--委托,C# .Net,c#,数据库,microsoft

  1. 委托可以通过New来实例化,要求传递一个和这个委托的参数和返回值完全匹配的方法【完全匹配】
  • 委托有几个参数,方法参数数量也要对应;参数的类型在顺序上也要一一对应
  • 委托 返回参数和 方法 返回的参数类型也需要对应
public delegate void NoReturnWithPara(int x, int y);
private static void NoReturnNoParaMehtod()
{
 Console.WriteLine("这是一个无参数无返回值的方法。。。");
}
  1. 委托的实例–可以直接指向一个和这个委托参数+返回值完全匹配的方法

-语法糖–编译器给我们提供额便捷功能–new WithReturnNoPara();省略掉了

WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);
//可以省略
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;
  1. 执行委托实例的Invoke方法–去执行这个委托实例化的指向的这个方法—执行方法
//调用
NoReturnWithPara noReturnWithPara = new NoReturnWithPara(NoReturnNoParaMehtod);
noReturnWithPara.Invoke(55,68);
  1. 可以执行这个实例内部的三个方法:
//如果委托定义没有参数;在Inovke也没有参数 
withReturnNoPara.Invoke();	

//开启一个新的线程去执行委托
withReturnNoPara.BeginInvoke(null, null);	

//回调
withReturnNoPara.EndInvoke();
  1. 多种实例化:new、 直接指向一个方法,指向一个Lambad表达式
//new
WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);

//直接指向一个方法
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;

//指向一个Lambad表达式
NoReturnWithPara noReturnWithPara3 = (x, y) => { };

三、框架内置委托 Func 和 Action

.NET Framework3.0时代就开始拥有的产物

1. Action

Action是来自于System.RunTime的一个声明好的可以带有一个或者多个参数的委托delegate,没有返回值的委托(最多支持16个入参)

private void NoreturnNopara()
{

}

Action action = new Action(NoreturnNopara);
action.Invoke(); 
private void DoNothingInt(int i)
{
    Console.WriteLine("This is DoNothing");
}

Action<int> action1 = new Action<int>(DoNothingInt);
action.Invoke(); 
Action<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action2 = null;

2. Func

  • 是来自于System.RunTime的一个声明好的可以有返回值的委托delegate,可以有参数 ,也可以没有参数(最多支持16个入参)

    private int ReturnNopara()
    {
        return 0;
    }
    
    Func<int> func = new Func<int>(ReturnNopara);
    
  • 如果既有参数 + 返回值,那<> 中前面类型参数=输入参数,最后的类型参数=返回值

    Func<int, int> func1 = new Func<int, int>(ToInt);
    
    Func<int, string, int> func2 = new Func<int, string, int>(DoNothingIntAndStringNew);
    
    Func<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int> func3 = null;
    

问题一: 我们自己是可以定义委托的,为什么系统框架要给我们提供这样的两个委托呢?

答案一:【做到委托类型的统一】既然是系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托,这样就可以把委托做到统一;我们以后在使用委托的时候,就不用自己再去定义这个委托了;

场景:

//正常
new Thread(new ThreadStart(DoNoting));
//报错
new Thread(new NoReturnNoPara(DoNoting));

//为什么ThreadStart 和 NoReturnNoPara都是没有参数没有返回的委托,但是传入NoReturnNoPara会报错呢?
//因为委托本质是一个类,你传入的实例类型和定义的参数类型不一致,当然会报错啦
//系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托

问题二: 那之前定义好的委托呢?

答案二: 【历史包袱】我们是去不掉的,这被称之为历史包袱;

**问题三:**什么情况下可以考虑使用委托?

**答案三:**方法内部业务逻辑耦合严重;如果多个方法中出现了很多重复代码–去除重复代码,逻辑重用,考虑委托,达到逻辑解耦的目的。

  • 从上而下:代码稳定,不需要修改;执行的逻辑不在同一个方法中–逻辑解耦
  • 从下而上:去掉了重复代码,逻辑重用

四、委托的嵌套使用(俄罗斯套娃)

Asp .Net Core的核心设计,委托实现中间件的应用;

俄罗斯套娃,属于番外装饰器–AOP支持;就是把委托这个箱子一层一层的包装起来,将要执行的核心业务逻辑封装在最内部的箱子中,执行顺序是从最外层开始到最内层,在一层一层执行出来。

每一层执行的时候,我们就可以增加一些其他的业务逻辑;-- AOP的支持,装饰器

经过特性+委托 多层嵌套的封装–程序的执行环节的自动装配;如果有十个环节,那么只需要定义10个特性,分别标记就可以增加一个处理环节–ASP.NET Core的管道处理模型。

C#基础--委托,C# .Net,c#,数据库,microsoft

普通类 InvokerAction.cs – Method方法

public class InvokerAction
{
    [BeforeWriteLogAttribute]
    [BeforeMethodAttribute]
    public void Method(int i)
    {
        Console.WriteLine("这里是要执行的核心业务逻辑");
    }
}

普通方式:

InvokerAction invokerAction = new InvokerAction();
invokerAction.Method(256);

反射:

InvokerAction invokerAction = new InvokerAction();
Type type = invokerAction.GetType();
MethodInfo methodInfo = type.GetMethod("Method");
methodInfo.Invoke(invokerAction, new object[] { 123 });

反射+委托:

public delegate void ShowDelegate();

public static void Show()
{
    InvokerAction invokerAction = new InvokerAction();
    Type type = invokerAction.GetType();
    MethodInfo methodInfo = type.GetMethod("Method");
    
    //实例化一个委托:委托内部包含了一个行为: 行为:执行Method方法
    ShowDelegate showMethod = new ShowDelegate(() =>
                                               {
                                                   methodInfo.Invoke(invokerAction, new object[] { 123 });
                                               }); 
    showMethod.Invoke();
}

套娃–多层委托的嵌套:

ShowDelegate showmthod1 = new ShowDelegate(() =>
                                           {
                                               showMthod.Invoke();
                                           });

ShowDelegate showmthod2 = new ShowDelegate(() =>
                                           {
                                               showmthod1.Invoke();
                                           });

ShowDelegate showmthod3 = new ShowDelegate(() =>
                                           {
                                               showmthod2.Invoke();
                                           });

ShowDelegate showmthod4 = new ShowDelegate(() =>
                                           {
                                               showmthod3.Invoke();
                                           });
//执行
showmthod4.Invoke();

定义一个特性:在特性中定义每个环节中要执行的动作:

抽象类–AbstractMethodAttribute.cs

public abstract class AbstractMethodAttribute : Attribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public abstract ShowDelegate Do(ShowDelegate action);
}

BeforeWriteLogAttribute.cs

public class BeforeWriteLogAttribute : AbstractMethodAttribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public override ShowDelegate Do(ShowDelegate action)
    { 
        ShowDelegate actionResult = new ShowDelegate(() =>
                                                     {
                                                         {
                                                             Console.WriteLine("前面写一个日志");  //这里可以随便写。。。。
                                                         }
                                                         action.Invoke();
                                                         {
                                                             Console.WriteLine("后面写一个日志");  //这里可以随便写。。。。
                                                         }
                                                     });
        return actionResult;
    }
}

BeforeMethodAttribute.cs

public class BeforeMethodAttribute : AbstractMethodAttribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public override ShowDelegate Do(ShowDelegate action)
    {
        ShowDelegate actionResult = new ShowDelegate(() =>
                                                     {
                                                         {
                                                             Console.WriteLine("在xxx 执行之前执行点业务逻辑");  //这里可以随便写。。。。
                                                         }

                                                         action.Invoke();

                                                         {
                                                             Console.WriteLine("在xxx 执行之后执行点业务逻辑");  //这里可以随便写。。。。
                                                         }
                                                     });
        //actionResult.Invoke(); 
        return actionResult;
    }
}

调用:

public static void Show()
{
    InvokerAction invokerAction = new InvokerAction();
	Type type = invokerAction.GetType();
	MethodInfo methodInfo = type.GetMethod("Method");
    
	ShowDelegate showMethod = new ShowDelegate(() =>
                                               {
                                                   methodInfo.Invoke(invokerAction, new object[] { 123 });
                                               }); 
    //如果方法上存在继承于自AbstractMethodAttribute的属性
    if (methodInfo.IsDefined(typeof(AbstractMethodAttribute), true))
    {
        //获取所有属性
        foreach (AbstractMethodAttribute attribute in methodInfo.GetCustomAttributes().Reverse())
        {
            //第一次循环时,showMethod.Invoke 交于第一个属性执行
            //接下来得每次执行,都是下一个属性执行上一个属性返回来得委托
            //最后形成一个俄罗斯套娃,值得注意的时候,这时候所有委托并没有实际执行
            showMethod = attribute.Do(showMethod);	
        }
    }
	//返回的最后一个委托开始执行,即从套娃的最外层开始执行,最终到 showMethod.Invoke -> methodInfo.Invoke;
    showMethod.Invoke();
}

五、多播委托

  1. 多播委托:我们声明的任何一个委托都是多播委托;任何一个委托都是继承自MulticastDelegate(多播委托),定义的所有的委托都是多播委托;

  2. 作用:可以通过+= 把多个方法添加到这个委托中去;形成一个方法的执行链;利用 action1.Invoke() 执行委托的时候,按照添加方法的顺序,依次去执行委托;

    Action action = new Action(DoNothing);
    action += DoNothingStatic;
    action += new Student().Study;
    action += Student.StudyAdvanced;
    action += DoNothing;
    action += () =>
    {
        Console.WriteLine("this is 拉姆达表达式。。。");
    };
    
  3. action.BeginInvoke(); //开启一个新的线程 去执行委托

    注册有多个方法的委托,不能使用BeginInvoke;如上述action, 因添加多个方法,所以不能使用 BeginInvoke直接执行,需要用到第四点的 action.GetInvocationList() 获取委托列表循环执行;

  4. 想要开启一个新的线程去执行委托呢?

    通过action.GetInvocationList() 获取到所有的委托,然后循环,每个方法执行的时候可以BeginInvoke

    foreach (Action action1 in action.GetInvocationList())
    {
        //action1.Invoke();
        action1.BeginInvoke(null,null);
    }
    
  5. 多播委托也可以通过-=移除方法

    是从后往前,逐个匹配,如果匹配不到,就不做任何操作就不做任何操作;如果匹配到,就把当前这个移除,且停止去继续往后匹配;

    action -= DoNothing;
    
  6. 在移除的方法的时候,必须是同一个实例,同一个方法才能移除;

    action -= new Student().Study;	//没有移除掉:因为不是同一个实例
    
  7. lambda表示式是不能移除的;

    虽然方法体内容是一样的,但是lambada表示在底层会生成不同的方法

    action -= () => //没有移除:  其实是因为不是同一个方法---lambda表达式--在底层会生成不同的方法
    {
        Console.WriteLine("this is 拉姆达表达式。。。");
    };
    

六、观察者模式

事例:猫的故事

Cat.cs

public void Miao()
{
    Console.WriteLine("{0} Miao", this.GetType().Name);
    new Dog().Wang(); //狗叫了
    new Mouse().Run();//老鼠跑了
    new Baby().Cry(); // 小孩哭了
}

缺点:

  • 职责不单一 — 一个类中包含的职责太多了,除了Miao 一声,依赖于其他的类太多;
  • 依赖太重–依赖于Dog、Mouse、…—代码不稳定;Dog、Mouse、…任何一个类的修改,都有可能会影响到这只猫;
  • 这完全是面向过程式编程–完全是翻译需求;

改造目的:

  • 职责单一: 猫的职责仅仅只是Miao一下,其他的动作,不是猫的动作;
  • 猫Miao一声后会引发一些列的动作,应该放出去,不能属于这只猫
  • 猫Miao一声后,触发一些列的动作—不能把这一些列的动作放在猫的内部

接口类 IObject.cs:

public interface IObject
{
    void DoAction();
}

观察者们: 【订阅者】

public class Dog : IObject
{
    public void DoAction()
    {
        this.Wang();
    }
    public void Wang()
    {
        Console.WriteLine("{0} Wang", this.GetType().Name);
    }
}
public class Mouse : IObject
{
    public void DoAction()
    {
        this.Run();
    }
    public void Run()
    {
        Console.WriteLine("{0} Run", this.GetType().Name);
    }
}
public class Baby : IObject
{
    public void DoAction()
    {
        this.Cry();
    }

    public void Cry()
    {
        Console.WriteLine("{0} Cry", this.GetType().Name);
    }
}

发布者1:Cat.cs 【委托】

  • 通过定义委托属性
  • 给委托+=方法–Miao一声之后,要触发的动作
  • 特点:把要执行的动作不放在猫的内部从外面去指定
  • 多播委托+=来完成了一系列动作的转移,把猫Miao一声后的动作转移到上端
  • 去掉了猫对于其他的依赖—提高了猫的稳定性
  • 通过委托实现了观察者模式–观察者模式—把不属于我的动作转移出去—甩锅
public  Action MiaoAction = null; 
/// <summary>
/// 这个方法仅仅只是Miao一声
/// 引发的动作---可以放到多播委托中去
/// </summary>
public void MiaoDelegate()
{
    Console.WriteLine("{0} MiaoDelegate", this.GetType().Name);
    MiaoAction?.Invoke();//?. 如果不为null,就执行后面的动作; 若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoAction += new Dog().Wang; //狗叫了
cat.MiaoAction += new Mouse().Run;//老鼠跑了
cat.MiaoAction += new Mother().Wispher;
cat.MiaoAction += new Baby().Cry; // 小孩哭了
cat.MiaoDelegate();	//执行1

cat.MiaoAction();	//和执行1一样,也可以执行

发布者2:Cat.cs 【接口 + 列表 + 循环执行】

public List<IObject> observerlist = new List<IObject>();	//IObject 含有 DoAction方法

public void MiaoObsever()
{
    Console.WriteLine("{0} MiaoObsever", this.GetType().Name);
    if (observerlist.Count>0)
    {
        foreach (var item in observerlist)
        {
            item.DoAction();
        }
    }
}
cat.observerlist.Add(new Dog()); //狗叫了
cat.observerlist.Add(new Mouse());//老鼠跑了
cat.observerlist.Add(new Baby()); // 小孩哭了
cat.MiaoObsever();

两者从本质上来说是没有什么太大的区别

第一种是通过多播委托来支持,是语法层面来支持;

第二种是通过面向对象的方法来完成,是程序设计层面来支持;

七、事件

发布者3:Cat.cs 【事件】

public event Action MiaoEventHanlder = null;

public void MiaoEnvent()
{
    Console.WriteLine("{0} MiaoEnvent", this.GetType().Name);
    MiaoEventHanlder?.Invoke();//若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoEventHanlder += new Dog().Wang; //狗叫了
cat.MiaoEventHanlder += new Mouse().Run;//老鼠跑了
cat.MiaoEventHanlder += new Mother().Wispher;
cat.MiaoEnvent(); 

cat.MiaoEventHanlder.Invoke(); //不能执行

问题一:委托和事件有什么联系和区别?

  • 事件是委托的实例,事件是特殊的委托;

  • 事件和委托都可以使用 +=-= ;

问题二:既然有了多播委托,为什么又搞个事件呢?

安全文章来源地址https://www.toymoban.com/news/detail-598690.html

  • 事件是只能在类的内部执行,就算是你的子列也不能执行当前类中的事件,只能由自己来执行
  • 事件从外面操作,只能有一个 += / -=

到了这里,关于C#基础--委托的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包