www.9778.com 2

www.9778.comC#语法——委托,架构的血液

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

 

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

如:

ThreadPool.QueueUserWorkItem(SomeAsyncTask,5);

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

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

ProcessDelegate process;

用委托回调方法

//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关键字。委托定义需要指明委托名称以及一个返回类型和一个参数列表

  • 初识委托
  • 用委托回调方法
  • 泛型委托
  • 简化语法

那么,为什么这个声明方式如此怪异呢,是因为,我们用delegate定义的变量,只能用函数赋值。赋值方式如下所示:

   3.  委托的各种写法


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

 

看Markdown效果支持的不大好。

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

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();
        }
    }

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

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

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

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


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

泛型委托

.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个Action委托,它们从无参数到最多16个参数,使用起来非常方便。如果需要返回值,可使用Func函数。如果需要使用ref或out关键字以传引用的方式传递参数,就需要自己定义委托了。

首先,我们创建父类代码如下:

示例:

标签(空格分隔): C#


声明委托类型的变量

初识委托

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

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

Action与Func是泛型委托,各支持16个入参变量。下面代码为一个入参的定义,多参数以此类推。

初始化委托变量时要把一个函数(此处Multiply为一个函数的名称)引用赋给委托变量,此函数需要具有与委托相同的返回类型和参数列表。c#使用上述略显古怪的语法,使用new关键字创建一个新的委托,参数为
要引用所需的函数,这是委托赋值的一个独特语法,函数名称是不带括号的

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

委托,架构的血液

  //定义委托
    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);
    }

简化语法

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);
    }
}
  • 委托可以调用多个方法,即一个委托变量可以引用多个函数,称为多路广播
  • 可以使用+=和-=运算符实现方法的增加和减少
  • 无返回值的委托,引用了多少个方法就会执行多少个方法。有返回值的委托同样会执行多个引用的方法,但返回的值是最后一个方法的返回值

但委托这种列车,是每个程序员都可以定义的,如果一个项目中有十个开发者,每个人都在定义委托,那么,就有可能出现定义了十个相同的委托的情况,这样就出现了撞车的现象。

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

C#语法——元组类型

 

Action与Func是微软为我们预先定义好了的,两个委托变量。其中Action是不带返回值的委托,Func是带返回值的委托。

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

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

初始化委托变量

其实,只要定义好委托,并将委托正确的传递,就可以实现穿越的调用了。

 

第一核心应用——随手线程:

process (param1,param2);

委托的线程应用是委托的第二种用法,分为线程使用委托,和委托的异步应用两种。

 

delegate string anonymousDelegate(int m, long num);
public static void Excute()
{
    anonymousDelegate ad = delegate (int m, long num) { return m.ToString() + num.ToString(); };//2.0时代的匿名委托
    anonymousDelegate ad2 = (m, num) => { return m.ToString() + num.ToString(); };//3.0以后匿名委托 
}

 

所以,让我们把委托刻到血液里吧。

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

委托是架构的血液,如果系统中没有委托,那代码将堆叠到一起,比大力胶粘的都紧密。

  2.  其他形式的委托

很简单,我们把delegate后面的 【void TestDelegate(string
message)】理解为一个变量,是不是就清晰明了了一些。

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

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
           });
    }
}

 

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

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;});

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

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

Action<int> a1 = (i) =>  { };
Func<string,int> f1 = (str) => {  return 1;//必须写 return 1; };
delegate double ParocessDelegate(double param1,double param2);

这里我们使用委托的BeginInvoke方法来开启线程,进行异步调用。如上面代码所示,这里介绍了Action与Func的最基础的异步应用。

process = Muiltiply;

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

定义委托

委托的基本应用

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

Action<string> action = HellowChinese;  
action("Spring.");  

看了上面的父子类后,是否感觉委托让我们繁杂的线程世界变简洁了呢?

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

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

示例

请记住,所有的回调函数,都是委托的穿越应用,所有的回调函数;都是委托的穿越应用;所有的回调函数,都是委托的穿越应用。

注意事项:

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

 

答案当然是,可以的。

 

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

 

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();
    }
}
  • 泛型委托
public delegate void TestDelegate(string message);
public delegate int TestDelegate(MyType m, long num);

还可以用另一种略微简单的语法

委托实际上是一种类型,是一种引用类型。

其中Action是无返回值的泛型委托,Func是有返回值的泛型委托,Predicate<>是返回bool类型的委托,他们都有多个参数的重载版本

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

  1. 使用委托
Action a1 = () => { };
Func<int> f1 = () => { return 1; };//必须写 return 1;
  • 匿名委托

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

 

这个应用,是最常见,也最普通的应用了。因为委托是引用类型,所以A类里定义的委托,可以在被内存回收之前,被其他类调用。

process =new ProcessDelegate(Multiply);
string result = td(51, 8);
Console.WriteLine(result);

就好比一碗汤面倒掉了所有的汤,只要它静放一个阵子,就会变成一坨面球,让你无从下嘴。

www.9778.com 1

delegate既然是关键字,和int,string一样,那么,为什么delegate后又跟了一个void或者int呢?

我们把delegate关键字理解为,是用来专门来定义这种复杂的变量的。而这种复杂的变量可以包含一个返回值和任意数目任意类型的传入参数。

我们先从刚介绍过的委托的线程应用说起。

运行结果如下:

那么委托到底是如何流动的呢?

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

public delegate void TestDelegate(string message);
public delegate long TestDelegate2(int m, long num);
public static void Excute()
{
    TestDelegate2 td = Double; 
} 
static long Double(int m, long num)
{
    return m * num;
}

重要的话要讲三遍。

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

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

Action与Func

www.9778.com 2

子类代码如下:

下面先看下声明代码,这里声明了两个委托。

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

世界上本没有回调函数,叫的人多了,也就有了。

如果委托是封装的函数,并且它又是引用类型。那么委托第一种常规的应用就浮现出来了。

委托是大家最常见的语法了,但会用与精通之间的差别是巨大的。

下面来看最简单的Action与Func的定义:

我们先看线程使用委托。如下代码所示,一个无入参匿名Action和一个无入参匿名Func。

 

如果函数是引用类型,那么这个函数只要没被内存回收,就可以被调用。如果是public函数或者是public
static函数,那么它能跨越的东西就更多了。

C#语法——await与async的正确打开方式

第三核心应用——回调函数:

后面我会继续写事件,消息,反射等语法,敬请期待。

因为委托的特质是引用类型,所以决定了委托是可以进行址传递。也就是说,委托是穿梭于我们系统代码中的列车。

结语

委托的线程应用

Action action = new Action(() => { });
IAsyncResult result = action.BeginInvoke((iar) =>
{
}, null);

Func<int> func = new Func<int>(() => { return 1; });  
IAsyncResult resultfunc = func.BeginInvoke((iar) =>
{
    var res = func.EndInvoke(iar); 
}, null);

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

匿名委托的官方介绍:在 2.0 之前的 C#
版本中,声明委托的唯一方式是使用命名方法。 C# 2.0 引入匿名方法,在 C#
3.0 及更高版本中,Lambda 表达式取代匿名方法作为编写内联代码的首选方式。

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

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


我们看到上面的代码,父类中添加了四个异步委托的调用函数,接下来,我们就可以在继承该类的子类中,随手开启线程了。

这样,你才能称为[Developer]。

换一种说法,就是委托封装了一个函数。

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

当你传递函数的时候,其实是匿名传递了一个委托的地址。

委托的使用方式如下:

有没有感觉,这个复杂的变量特别像一个函数的定义。

没错,它们的确是一样的。因为委托是用函数来赋值的,所以调用方式一样也并不奇怪,不是吗。

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

没错,官方定义,委托类型的声明与方法签名相似。所以,这个复杂变量,的确,书写的方式就是与函数一样。

下面我们看下穿越应用的代码。

什么是委托?

匿名委托的应用

那我们的委托的第一个核心应用,就是父类的公共函数,线程随手启动。如何随手开启呢?

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

这里我们会发现,委托的使用方式与函数调用一样。

看不懂没关系,我们直接来学习使用。代码如下:

委托的定义

本篇文章主要介绍委托的应用。

比如可以跨类调用,跨程序集调用等等。而这种用法,就是委托的基本应用。

第二核心应用——穿越你的世界:

我们能看到两种委托应用,代码都非常简洁。