开发者社区


项目管理 | IT/商务管理 | 职涯
首页 - 管理和职涯
管理&职涯
全文搜索:   

C#的代理和事件
作者: Eric Gunnerson
2001-09-24 14:28:31



代理 (Delegate)
大多数情况下,当调用函数时我们会指定要直接调用的函数。比如类 MyClass 如具有一个名为 Process 的函数,我们通常会按如下方法进行调用:

MyClass myClass = new MyClass();
myClass.Process();


这种调用在大多数情况下都是可行的。但是有些时候,我们不想直接调用函数,而希望能够将它传递给其他人,让他们进行调用。在以事件驱动的系统(如图形用户界面)中,这种方法尤为有用。例如当我需要在用户单击某个按钮即可执行一些代码时,或者当我要记录一些信息但却无法指定记录方式时。
考虑以下示例:

public class MyClass
{
    public void Process()
    {
        Console.WriteLine("Process() begin");
        // 这里还有其他东西...
        Console.WriteLine("Process() end");
    }
}


在此类中,我们进行一些记录,以了解函数的开始时间和结束时间。但是,我们的记录仅限于发送到控制台,这可能不是我们所需要的。我们真正需要的是能够控制从函数外部记录信息的位置,同时不必使函数代码变得复杂。

在这种情况下,代理便是理想的解决方法。代理使我们可以指定将要调用的函数,看起来好像不需要指定哪个函数一样。对代理的声明类似于对函数的声明,不同的是在这种情况下,我们所声明的是此代理可引用的函数签名。

我们的例子将声明一个带有单个字符串参数且没有返回类型的代理。修改该类如下:

public class MyClass
{
    public delegate void LogHandler(string message);
    public void Process(LogHandler logHandler)
    {
        if (logHandler != null)
        logHandler("Process() begin");
        // 这里还有其他东西
        if (logHandler != null)
        logHandler ("Process() end");
    }
}


使用代理与直接调用函数相似。只是在调用函数前,我们需要检查代理是否为空(即不指向一个函数)。

要调用 Process() 函数,我们需要声明一个与代理相匹配的记录函数,然后创建代理的实例,以指向该函数。然后,将此代理传递给 Process() 函数。


class Test
{
    static void Logger(string s)

    {
        Console.WriteLine(s);
    }

    public static void Main()
    {
        MyClass myClass = new MyClass();

        MyClass.LogHandler lh = new MyClass.LogHandler(Logger);

        myClass.Process(lh);
    }
}

Logger() 函数是一个我们要从 Process() 函数中调用的函数,我们对它进行了声明,使其与代理相匹配。在 Main() 中,我们创建代理的一个实例,然后将该函数传递给代理构造函数,使其指向该函数。最后,我们将代理传递给 Process() 函数,该函数接着调用 Logger() 函数。

如果您习惯于使用 C++ 语言,您可能会认为代理很像函数指针,这种想法非常接近于事实。但是,代理并不“仅仅”是函数指针,它还提供了其它多种功能。


传递状态 (Passing State)

在上面的简单示例中,Logger() 函数仅仅是输出些字符串。一个不同的函数可能把信息记录到文件中,但是要进行这种操作,该函数需要知道把信息写道什么文件中。

对于 Win32® 而言,当您传递函数指针时,可随之传递状态。但是对于 C#,这就没有必要了,因为代理既可指向静态函数,“也”可指向成员函数。以下是一个有关如何指向成员函数的示例:

class FileLogger
{
    FileStream fileStream;
    StreamWriter streamWriter;

    public FileLogger(string filename)
    {
        fileStream = new FileStream(filename, FileMode.Create);
        streamWriter = new StreamWriter(fileStream);
    }

    public void Logger(string s)
    {
        streamWriter.WriteLine(s);
    }

    public void Close()
    {
        streamWriter.Close();
        fileStream.Close();
    }
}

class Test
{
    public static void Main()
    {
        FileLogger fl = new FileLogger("process.log");

        MyClass myClass = new MyClass();

        MyClass.LogHandler lh = new MyClass.LogHandler(fl.Logger);

        myClass.Process(lh);
        fl.Close();
    }
}


FileLogger 类仅封装文件。我们修改了Main()以使代理指向 FileLogger 的 fl 实例的 Logger() 函数。当从 Process() 中激活此代理时,将会调用成员函数并把字符串记录到相应的文件中。

其优点在于,我们不必更改 Process() 函数 -对代理来说代码都是相同的,无论引用的是静态函数还是成员函数,。

【下一页】
声明:
Builder.com.cn(原ZDNet China应用开发频道)原创文章版权所有,未经许可严禁转载,且不构成投资建议。
近期相关报道:
实用技术文档
J2me XML
C/C++ C#
Java Oracle
Mysql .Net
VB.NET CSS
SQL Server 数据库
SQL UNIX
Linux Jsp
PHP Perl
Javascript IIS
XHTML ColdFusion
ASP/ASP.NET Apache
AJAX
订阅技术邮件
订阅"技术圈"杂志!请在下面选择您感兴趣的专题,填写e-mail地址,然后按订阅按钮:
应用开发管理
VS.NET 周刊
Database 周刊
WEB Service周刊
JAVA 周刊
IT 认证
Windows服务器周刊
互联网开发
当Windows Server 2008专家得5000元现金大奖
CNET NETWORKS 中国: 爱卡汽车网 | CNET科技资讯网 | e询网 | CWEEK | 蜂鸟网 | GameSpot China | 个人电脑 | PChome | SPN | 网友世界 | ZDNet China | 中关村在线
CNET NETWORKS 美国: BNET | CNET.com | CNET Download.com | CNET News.com | CNET Reviews | CNET Shopper.com | GameSpot | MP3.com | mySimon | Release 1.0 | Search.com | TechRepublic | TV.com | Webshots | ZDNet
Copyright (c) 2006 CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
ZDNet 公司标识是 CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号