关于“回调”的实现

callback基础:

回调机制包括带委托的成员、虚拟化的成员、基于接口的插件。事件是回调最常用的形式,当事件raised时,调用绑定方法的事件句柄的实例。

 

在编程机制或函数调用模式上,一般分成三种,同步调用、回调和异步调用。回调在不同的语言中有不同实现。具体在C#中,是使用以C为基础的语言环境特有的委托来实现回调,而通过AsyncCallback亦可实现异步调用。委托是实现调用者与被调用实例间解耦(decoupling)的工具。

 

delegate定义:

委托类型由四个成员组成,编译器会自动提供一个这样的完整类,包括:constructor,Invoke方法,BeginInvoke方法和EndInvoke方法。

 

与委托一切相关的本质:

F(f)  即函数的函数,其中f经抽象为委托,变形为F(delegate);F进一步抽象为处理,最后为handler(delegate)。MS为函数代理专门制造出了委托概念,带有浓重的人为斧凿痕迹,这样大的代价,必定有MS的理由吧。

 

下面我们以静态函数为例,论述一下它的原理: 

1、将彼此具有相同结构的函数(如函数Fa和Fb,)归为(classify)一个类别,类型(type)为引用类型;经过语言封装,赋予一个标识,就叫作"delegate"吧;

 

2、对具体执行函数Fa或Fb进行调用:用某处理函数F封装对Fa或Fb的处理,以"delegate"(即同构函数的指针-以"某个delegate"作为代名词)作参,传递给该函数;在F中对该"delegate"进行处理(这里的函数F是一个重要的'proxy').

public delegate void EnumConnectionsCallback(DBConnection connection);
public void EnumConnections(EnumConnectionsCallback callback)
{
  foreach (DBConnection connection in activeConnections)
  {
        
    callback(connection);
     //上句,系统默认自动调用callback.Invoke(connection)
  }
 }

 

 

 3、接下来,定义该同构函数("delegate")的某具体函数的行为,以便在上述封装中进行反向调用。

 

  4、于客户端实例化委托。通过以上步骤,能够将所有使用相同结构(数字签名)的函数,映射到某个"delegate"并由系统自动处理细节,但函数行为可以完全不同。

小结: 我们看到委托实现回调,基本可归纳为这样几个步骤,来达到解耦:

(1)定义头(header)--委托 

(2)定义实际caller

(3)定义具体处理--函数

如果把函数理解为特定函数如EventHandler(object sender,EventArgs e),那么以上也就是事件类型的原型,所以,事件是对内置委托、EventHandler处理函数、环境参数(context argument=EventArgs)的(publisher-subscriber pattern)封装,可直接使用。(关于事件类型的详细分析

  

完整的原型用例:

 

using System;
using System.Collections;

class DBConnection
{
    protected static int NextConnectionNbr = 1;

    protected string connectionName;
    public string ConnectionName
    {
        get
        {
            return connectionName;
        }
    }

    public DBConnection()
    {
        connectionName = "Database Connection " 
            + DBConnection.NextConnectionNbr++;
    }
}

class DBManager
{
    protected ArrayList activeConnections;
    public DBManager()
    {
        activeConnections = new ArrayList();
        for (int i = 1; i < 6; i++)
        {
            activeConnections.Add(new DBConnection());
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
};

 

class InstanceDelegate
{
    public static void PrintConnections(DBConnection connection)
    {
        Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
            connection.ConnectionName);
    }

    public static void Main()
    {
        DBManager dbManager = new DBManager();

        Console.WriteLine("[Main] Instantiating the " +
            "delegate method");
          DBManager.EnumConnectionsCallback _printConnections =
             new DBManager.EnumConnectionsCallback(PrintConnections);

        Console.WriteLine("[Main] Calling EnumConnections " +
            "- passing the delegate");
          dbManager.EnumConnections(_printConnections);

        Console.ReadLine();
    }
};

小结: 委托其实是C#中,对同构函数进行抽象的语法糖(OOP),也可称为对方法进行引用的类型。

 

下面我们对委托“原型”进行初步简化。能进行这种简化的原因是“委托推断(delegate inference)”,即系统自动创建委托对象。

 

using System;
using System.Collections;

class DBConnection
{
    protected static int NextConnectionNbr = 1;

    protected string connectionName;
    public string ConnectionName
    {
        get
        {
            return connectionName;
        }
    }

    public DBConnection()
    {
        connectionName = "Database Connection " 
            + DBConnection.NextConnectionNbr++;
    }
}

class DBManager
{
    protected ArrayList activeConnections;
    public DBManager()
    {
        activeConnections = new ArrayList();
        for (int i = 1; i < 6; i++)
        {
            activeConnections.Add(new DBConnection());
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
};

class InstanceDelegate
{
    public static void PrintConnections(DBConnection connection)
    {
        Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
            connection.ConnectionName);
    }

    public static void Main()
    {
        DBManager dbManager = new DBManager();

        Console.WriteLine("[Main] Instantiating the " +
            "delegate method");

/*          DBManager.EnumConnectionsCallback _printConnections =
             new DBManager.EnumConnectionsCallback(PrintConnections);*/
            //使用委托推断,直接赋予实例化的委托以函数名称
        DBManager.EnumConnectionsCallback _printConnections = PrintConnections;

        Console.WriteLine("[Main] Calling EnumConnections " +
            "- passing the delegate");
          dbManager.EnumConnections(_printConnections);

        Console.ReadLine();
    }
};

  

总结:利用委托实现回调时,虽语法繁琐,但效率颇高。C#终究是一种强类型的语言,有代价是正常。而我们可以通过“委托推断”等"shortcut"措施对其语法进行简化。

 

委托本质上是对函数的一种归集和反射实现;而事件是组件间通用的消息类型,也是组件关系解耦的模型。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值