c# 委托 delegate

2019-12-07 23:21栏目:编程
TAG:

我们在做开发的时候,一定接触过父类。父类是干什么的呢?父类通常是用来编写公共属性和函数,方便子类调用的。

委托是一种存储函数引用的类型,在事件和事件的处理时有重要的用途



定义委托

简化语法

学会了赋值以后,我开始使用委托。

有了引用函数的委托变量之后,我们就可以用委托变量调用Muiltiply函数;也可以把委托变量传递给其他函数

简化语法2:不需要定义回调方法(lambda表达式)

前面代码中,回调方法名称SomeAsyncTask传给ThreadPool的QueueUserWorkItem方法。如果方法较为简单可以直接写为:

ThreadPool.QueueUserWorkItem(obj=>Console.WriteLine(Obj),5);

编译器在看到则个lambda表达式后会生成一个匿名方法。新的语言规范建议开发人员多多使用lambda表达式语法。
书本中的用委托回调多个方法没有总结,个人觉得现在一个方法已经差不多了,等理解的好了再去研究调用多个方法。
文章另外地址:

如代码所示,匿名委托是Lambda表达式,不懂的同学就当它是有固定写法即可,不用讲什么道理,只要记住并应用即可。

 

看Markdown效果支持的不大好。

public delegate void TestDelegate(string message);
public delegate int TestDelegate(MyType m, long num);

示例:

泛型委托

.NET Framework现在支持泛型,如返回void可用下面泛型

public delegate void Action();
public delegate void Action(T obj);
public delegate void Action(T1 arg1,T2 arg2);
public delegate void Action(T1 arg1,T2 arg2,T3 arg3);
...

事实上,.NET Framework现在提供了17个Actionbob体育app,委托,它们从无参数到最多16个参数,使用起来非常方便。如果需要返回值,可使用Func函数。如果需要使用ref或out关键字以传引用的方式传递参数,就需要自己定义委托了。

C#语法——泛型的多种应用

初始化委托变量

  • 初识委托
  • bob体育平台,用委托回调方法
  • 泛型委托
  • 简化语法

委托是我们最常用的语法,它将函数封装成引用类型的变量,供其他单位调用。

public delegate int DelegateProcess(int num1, int num2);

//第一种写法
DelegateProcess process= new DelegateProcess(Multiply);

//第二种写法
DelegateProcess process= Multiply;

//第三种写法  匿名委托
DelegateProcess process= delegate(int a,int b)
{
   return a*b;
}

//第四种写法 Lamdba表达式
DelegateProcess process =((int a,int b)=>{return a*b;});

//第五种写法 Action<T>和Func<T>
Action<int,int> process= ((int a,int b)=>{Console.WriteLine(a * b);});
Func<int,int,int> process= ((int a,int b)=>{return a*b;});

标签(空格分隔): C#

匿名委托虽然减少了一点代码,但还是要求我们自己去声明委托。所有,还能再简写一点吗?

声明委托类型的变量

买来《CLR Via C#》这本书很久了,一直也没有对其进行总结,看的非常凌乱,趁此机会好好总结一下,也算对C#学习的一个总结。

也就是说,我们可以不用再去自己手动声明委托了。

process =new ProcessDelegate(Multiply);

初识委托

委托类型 (delegate type) 表示对具有特定参数列表和返回类型的方法的引用(个人觉得这句话对委托的解释非常好)。通过委托,我们能够将方法作为实体赋值给变量和作为参数传递。委托类似于在其他某些语言中的函数指针的概念。.NET Framework通过委托来提供回调函数机制,委托确保了回调函数是类型安全的。
调用一个委托的方法:

  1. 声明一个委托类型
  2. 声明一个方法包含要执行的代码
  3. 创建一个委托实例
  4. 调用这个委托实例

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的【推荐】,非常感谢!
如果您觉得这篇文章对您有所帮助,那就不妨支付宝小小打赏一下吧。 

 

用委托回调方法

//1、声明一个委托实例
internal sealed class DelegateIntro {
    internal delegate void Feedback(Int32 value);
    private static void StaticDelegateDemo() {
          Console.WriteLine("----- Static Delegate Demo -----");
          //传递的为NUll,处理每个数据项都不调用回调方法
          Counter(1, 3, null);
          //3、创建静态的委托实例,用委托回调静态方法
          Counter(1, 3, new Feedback(DelegateIntro.FeedbackToConsole));
          Counter(1, 3, new Feedback(FeedbackToMsgBox)); // "Program." is optional
          Console.WriteLine();
       }

    private static void InstanceDelegateDemo() {
          Console.WriteLine("----- Instance Delegate Demo -----");
          //3、创建实例委托,用委托回调实例方法
          DelegateIntro di = new DelegateIntro();
          Counter(1, 3, new Feedback(di.FeedbackToFile));

          Console.WriteLine();
       }

    private static void Counter(Int32 from, Int32 to, Feedback fb)     {
          for (Int32 val = from; val <= to; val++) {
             // If any callbacks are specified, call them
             if (fb != null)
             //4、调用这个委托
                fb(val);
          }
       }
    //2、声明一个方法包含要执行的代码
    private static void FeedbackToConsole(Int32 value) {
          Console.WriteLine("Item=" + value);
       }
    //2、声明一个方法包含要执行的代码
    private static void FeedbackToMsgBox(Int32 value) {
          MessageBox.Show("Item=" + value);
       }

    private void FeedbackToFile(Int32 value) {
          StreamWriter sw = new StreamWriter("Status", true);
          sw.WriteLine("Item=" + value);
          sw.Close();
       }
}

委托对象是方法的包装器(wrapper),是方法能通过包装器来间接回调。如上的FeedbackToConsoleFeedbackToMsgBox方法通过委托包装,通过Counter方法来间接回调。
这个例子中的所有操作都是类型安全的。例如:在构造Feedback委托对象时,编译器确保FeedbackToConsoleFeedbackToMsgBox方法的签名兼容于Feedback委托类型定义的签名。具体的说,两个方法都要获取一个参数(一个int32),而且两者都熬有相同的返回类型(Void),将FeedbackToConsole的定义改为下面这样
private static Boolean FeedbackToCOnsole(string value){
···
}
C#编译器将不会编译以上代码,并报告一下错误:

error CS0123:"FeedbackToConsole"的重载均与委托"Feedback"不匹配

将方法绑定到委托时,C#和CLR都允许引用类型的协变性逆变性协变性是指方法能返回从委托的返回类型派生的一个类型。逆变性是指方法获取的参数可以是委托的参数类型的基类。
比如:

delegate object Mycallback(fileStream s);

完全可以构造该委托类型的一个实例并绑定到具有以下原型的方法

String SomeMethod(Stream s);

在这里,SomeMethod的返回类型String派生自委托的返回类型(Object),这是协变性;SomeMethod的参数类型Stream是委托的参数类型FileStream的基类,这是逆变性
注意只有引用类型才支持协变性和逆变性。

所以,理论上,只要我们利用好委托,就可以大量减少冗余的代码。

  //定义委托
    delegate string lookMe(string s);

    protected void LinkButton1_Click(object sender, EventArgs e)
    {
        //匿名委托
        lookMe lm = delegate(string name) { return "亲爱的 " + name + ",请看着我的眼睛!"; };

        //匿名委托调用
        string name1 = "jarod";
        Label1.Text = lm(name1);
    }

简化语法1:不要构造委托对象。

如:

ThreadPool.QueueUserWorkItem(SomeAsyncTask,5);

本来ThreadPool类的静态QueueUserWorkItem方法期待一个WaitCallback委托对象的引用,但你现在直接可以传递一个方法符合waitCallback类型就可以了。但C#编译器其实还是会生成waitcallback委托对象--只是语法简化了而已。

我们可以在列车上放很多很多东西,在需要的站点,叫停列车,并将托运的东西搬下来使用。

 

如果需要公共委托,可以采取反射的方式来调用。

匿名委托使用起来更简洁一点,不用在定义一个专用的委托函数来传递方法,也更可以更好的理解委托

我们经常会在各种论坛看到有人发问,A页面如何调用B页面的属性、方法、父页面获取子页面的属性、方法,或者子页面获取父页面的属性、方法。

 

class ChildDelegateSyntax : BaseDelegateSyntax
{
    public void Excute()
    {
        //开启异步方法
        base.AsyncLoad(() => { });

        //开启异步方法,并且在异步结束后,触发回调方法
        base.AsyncLoad(() => { },
            ()=> 
            {
                //我是回调方法
            });

        //开启异步有入参的方法,传递参数,并且在异步结束后,触发回调方法
        base.AsyncLoad<string>((s) => { },"Kiba518",
           () =>
           {
                //我是回调方法
           });

        //开启异步有入参的方法,传递字符串参数Kiba518,之后返回int型结果518,
        //并且在异步结束后,触发回调方法,回调函数中可以获得结果518
        base.AsyncLoad<string,int>((s) => {
            return 518;
        }, "Kiba518",
           (result) =>
           {
               //我是回调方法 result是返回值518
           });
    }
}
namespace Delegate
{

        public delegate int Call(int num1, int num2);//第一步:定义委托类型
        class SimpleMath
        {
            // 乘法方法
            public int Multiply(int num1, int num2)
            {
                return num1 * num2;
            }

            // 除法方法
            public int Divide(int num1, int num2)
            {
                return num1 / num2;
            }
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Call objCall;//第二步:声明委托变量
            // Math 类的对象
            SimpleMath objMath = new SimpleMath(); 
            // 第三步:初始化委托变量,将方法与委托关联起来
            objCall = new Call(objMath.Multiply);


            objCall += objMath.Divide;//向委托增加一个方法
            //objCall -=  objMath.Divide;//向委托减去一个方法

            // 调用委托实例,先执行objMath.Multiply,然后执行objMath.Divide
            int result = objCall(5, 3);
            System.Console.WriteLine("结果为 {0}", result);
            Console.ReadKey();
        }
    }

可以说,Action与Func完全包含了,我们日常使用所需的,全部的,委托变量。

  2.  其他形式的委托

微软用delegate关键字来声明委托,delegate与int,string,double等关键字一样。都是声明用的。

public static void HellowChinese(string strChinese)  
{  
    Console.WriteLine("Good morning," + strChinese);  
    Console.ReadLine();  
}  

Action<string> action = HellowChinese;  
action("Spring.");  
public class FirstDelegateSyntax
{
    public FirstDelegateSyntax()
    {
        Console.WriteLine(" First 开始 "  );
        SecondDelegateSyntax sds = new SecondDelegateSyntax(()=> {
            Console.WriteLine(" First传给Second委托被触发 ");
        });
        sds.Excute();
        Console.WriteLine(" First 结束 ");
    }
}

public class SecondDelegateSyntax
{
    public Action Action { get; set; }
    public SecondDelegateSyntax(Action _action)
    {
        Console.WriteLine(" Second的构造函数 ");
        Action = _action;
    }
    public void Excute()
    {
        Console.WriteLine(" Second的Excute被触发 ");
        Action();
    }
}

 


  • 匿名委托

因为委托是引用类型,所以可以被[址传递]。函数是不可以被传递的。

  • 泛型委托

那就是——引用类型的函数。

Action<>,Func<>,Predicate<> 其实他们都是委托代理的简写形式,通过他们可以省去定义委托的步骤

如果他们是同等地位的关键字,为什么可以一起使用呢?

通俗的说,委托是一个可以引用方法的类型,当创建一个委托,也就创建一个引用方法的变量,进而就可以调用那个方法,即委托可以调用它所指的方法。

我们可以看到,我们传递的委托,穿越了自身所属的类。在SecondDelegateSyntax类中被触发了。

委托的使用需要以下步骤:

C#语法——元组类型

process = Muiltiply;

子类代码如下:

定义了委托后,就可以声明一个该委托类型的变量

所以委托在使用的时候,尽量做到有序传递,即预先做好列车的行驶路线,让委托按照路径运行。尽量不要定义可以被任何单位调用的公共委托。

 

运行结果如下:

   3.  委托的各种写法

所以,委托是架构的血液,是框架的流畅的基石。

 

下面我们再来看委托的异步应用。首先看最简单的异步调用。

 

委托的定义

示例

Task taskAction = new Task(() => { });//无入参匿名Action
taskAction.Start(); 
Task<int> taskFunc = new Task<int>(() => { return 1; });//无入参匿名Func
taskFunc.Start();
int result= taskFunc.GetAwaiter().GetResult();//获取线程返回结果
delegate double ParocessDelegate(double param1,double param2);

一个程序员如果不能精通委托,那么,他永远无法成为高级程序员。

 

接下来,我们来看委托的第二种核心用法,穿越的应用。

  1. 使用委托
class BaseDelegateSyntax
{ 
    public void AsyncLoad(Action action)
    {

    }
    public void AsyncLoad(Action action, Action callback)
    {
        IAsyncResult result = action.BeginInvoke((iar) =>
        {
            callback();
        }, null);
    }
    public void AsyncLoad<T>(Action<T> action, T para, Action callback)
    {
        IAsyncResult result = action.BeginInvoke(para, (iar) =>
        {
            callback();
        }, null);
    }
    public void AsyncLoad<T, R>(Func<T, R> action, T para, Action<R> callback)
    {
        IAsyncResult result = action.BeginInvoke(para, (iar) =>
        {
            var res = action.EndInvoke(iar);
            callback(res);
        }, null);
    }
}
process (param1,param2);

版权声明:本文由bob体育app发布于编程,转载请注明出处:c# 委托 delegate