 |
|
|
代理 (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() 函数 -对代理来说代码都是相同的,无论引用的是静态函数还是成员函数,。
|