C# 关于C语言中的事件和委托#
将事件视为代理类型的实例有什么副作用 Jon Skeet说,“事件不是委托实例。”,。如果我在其他地方读过这篇文章,我就不会问这个问题 在过去的两个月里,我一直使用event关键字将事件可视化为特殊类型的委托,这有助于防止通过事件调用的委托无效 有没有人能详细说明一下,对于C#和基于事件的编程新手来说,如何正确地将整个概念形象化 编辑1:C# 关于C语言中的事件和委托#,c#,events,design-patterns,delegates,C#,Events,Design Patterns,Delegates,将事件视为代理类型的实例有什么副作用 Jon Skeet说,“事件不是委托实例。”,。如果我在其他地方读过这篇文章,我就不会问这个问题 在过去的两个月里,我一直使用event关键字将事件可视化为特殊类型的委托,这有助于防止通过事件调用的委托无效 有没有人能详细说明一下,对于C#和基于事件的编程新手来说,如何正确地将整个概念形象化 编辑1: namespace DoSomethingLibrary { /* *This is a public delegate declared
namespace DoSomethingLibrary
{
/*
*This is a public delegate declared at the base namespace level for global presence.
*The question is WHY do we need to have a DELEGATE here?
*The answer is: I do not want to implement the LOGGING logic. Why? Well, my consumers are many
*and all are equally demanding. They all need different types of logging. Some need HTML logging,
*some need XML logging for their custom log analyzer, some need plain text logging etc...
*This is hell for me. How am I going to support all their demands. I cannot. Thus, I ask them to
*implement LOGGING on their side. I am providing an INTERFACE(literal sense) in the guise of a DELEGATE.
*A DELEGATE is a HOOK.
*This is the hook that is needed for consumers to hook their custom loggers into the library.
*/
public delegate void Logger(string firstParam, string secondParam);
public class PrintingManiac
{
public Logger printingManiacConsumerLoggerHook;
public void StartPrintingLikeAManiac()
{
for (int iterator = 0; iterator <= 3; iterator++)
{
/*This loop is an emulator which I am using to emulate some huge processing or some huge job.
*Let us imagine that this is a library that does some heavy data crunching OR some
*extremely complex data access job etc..
*/
Console.WriteLine("Actual WORK - " + iterator.ToString());
/*After each step this library tries to LOG. But NOTE that this library
*has no LOGGER implemented. Instead, this library has judiciously DELEGATED
*the logging responsibilty to the CONSUMER of this library.
*/
printingManiacConsumerLoggerHook("Actual Work", "Step " + iterator.ToString());
}
}
}
}
/*
* Let us assume that I have purchased the DoSomethingLibrary DLL from a vendor.
* I have to add the DLL as a reference to my executable's project in Visual Studio.
* I also have to use the DoSomethingLibrary namespace to access the Logic in the DLL.
*/
using DoSomethingLibrary;
namespace UnderstandingDelegates
{
class Program
{
static void Main(string[] args)
{
/*
* Creating an object of the lone class PrintingManiac in the DoSomethingLibrary
*/
PrintingManiac newManiac = new PrintingManiac();
/*
* HOOKING my custom logger to the DoSomethingLibrary DLL.
* I get the best of both the worlds. I have a well-tested and efficient library working for me
* AND I have the best logging avaliable.
* The DoSomethingLibrary DLL has no knowledge of what logging this executable is going to use.
* This executable has to just satisfy the requirements of the DELEGATE signature of DoSomethingLibrary DLL.
*/
newManiac.printingManiacConsumerLoggerHook += new Logger(ClientsCustomizedLoggerTwo);
newManiac.StartPrintingLikeAManiac();
Console.ReadLine();
}
public static void ClientsCustomizedLoggerOne(string firstParam, string secondParam)
{
/*
*This logger has '=' used as a decorator
*In real scenarios the logger may be very complex.
*Let us assume this is an HTML logger
*/
Console.WriteLine("=============================");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("=============================");
}
public static void ClientsCustomizedLoggerTwo(string firstParam, string secondParam)
{
/*
*This logger has '-' used as a decorator
*Let us assume this is an XML logger
*/
Console.WriteLine("------------------------------");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("------------------------------");
}
}
}
我理解了代表的概念,因此事件变成了一个非常简单的概念。我想继续并添加一个我在与这些构造的战斗中变戏法的例子。为了更好地理解,我添加了很多评论。这是为像我这样的新人准备的:
库DLL:
namespace DoSomethingLibrary
{
/*
*This is a public delegate declared at the base namespace level for global presence.
*The question is WHY do we need to have a DELEGATE here?
*The answer is: I do not want to implement the LOGGING logic. Why? Well, my consumers are many
*and all are equally demanding. They all need different types of logging. Some need HTML logging,
*some need XML logging for their custom log analyzer, some need plain text logging etc...
*This is hell for me. How am I going to support all their demands. I cannot. Thus, I ask them to
*implement LOGGING on their side. I am providing an INTERFACE(literal sense) in the guise of a DELEGATE.
*A DELEGATE is a HOOK.
*This is the hook that is needed for consumers to hook their custom loggers into the library.
*/
public delegate void Logger(string firstParam, string secondParam);
public class PrintingManiac
{
public Logger printingManiacConsumerLoggerHook;
public void StartPrintingLikeAManiac()
{
for (int iterator = 0; iterator <= 3; iterator++)
{
/*This loop is an emulator which I am using to emulate some huge processing or some huge job.
*Let us imagine that this is a library that does some heavy data crunching OR some
*extremely complex data access job etc..
*/
Console.WriteLine("Actual WORK - " + iterator.ToString());
/*After each step this library tries to LOG. But NOTE that this library
*has no LOGGER implemented. Instead, this library has judiciously DELEGATED
*the logging responsibilty to the CONSUMER of this library.
*/
printingManiacConsumerLoggerHook("Actual Work", "Step " + iterator.ToString());
}
}
}
}
/*
* Let us assume that I have purchased the DoSomethingLibrary DLL from a vendor.
* I have to add the DLL as a reference to my executable's project in Visual Studio.
* I also have to use the DoSomethingLibrary namespace to access the Logic in the DLL.
*/
using DoSomethingLibrary;
namespace UnderstandingDelegates
{
class Program
{
static void Main(string[] args)
{
/*
* Creating an object of the lone class PrintingManiac in the DoSomethingLibrary
*/
PrintingManiac newManiac = new PrintingManiac();
/*
* HOOKING my custom logger to the DoSomethingLibrary DLL.
* I get the best of both the worlds. I have a well-tested and efficient library working for me
* AND I have the best logging avaliable.
* The DoSomethingLibrary DLL has no knowledge of what logging this executable is going to use.
* This executable has to just satisfy the requirements of the DELEGATE signature of DoSomethingLibrary DLL.
*/
newManiac.printingManiacConsumerLoggerHook += new Logger(ClientsCustomizedLoggerTwo);
newManiac.StartPrintingLikeAManiac();
Console.ReadLine();
}
public static void ClientsCustomizedLoggerOne(string firstParam, string secondParam)
{
/*
*This logger has '=' used as a decorator
*In real scenarios the logger may be very complex.
*Let us assume this is an HTML logger
*/
Console.WriteLine("=============================");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("=============================");
}
public static void ClientsCustomizedLoggerTwo(string firstParam, string secondParam)
{
/*
*This logger has '-' used as a decorator
*Let us assume this is an XML logger
*/
Console.WriteLine("------------------------------");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("------------------------------");
}
}
}
编辑2:
namespace DoSomethingLibrary
{
/*
*This is a public delegate declared at the base namespace level for global presence.
*The question is WHY do we need to have a DELEGATE here?
*The answer is: I do not want to implement the LOGGING logic. Why? Well, my consumers are many
*and all are equally demanding. They all need different types of logging. Some need HTML logging,
*some need XML logging for their custom log analyzer, some need plain text logging etc...
*This is hell for me. How am I going to support all their demands. I cannot. Thus, I ask them to
*implement LOGGING on their side. I am providing an INTERFACE(literal sense) in the guise of a DELEGATE.
*A DELEGATE is a HOOK.
*This is the hook that is needed for consumers to hook their custom loggers into the library.
*/
public delegate void Logger(string firstParam, string secondParam);
public class PrintingManiac
{
public Logger printingManiacConsumerLoggerHook;
public void StartPrintingLikeAManiac()
{
for (int iterator = 0; iterator <= 3; iterator++)
{
/*This loop is an emulator which I am using to emulate some huge processing or some huge job.
*Let us imagine that this is a library that does some heavy data crunching OR some
*extremely complex data access job etc..
*/
Console.WriteLine("Actual WORK - " + iterator.ToString());
/*After each step this library tries to LOG. But NOTE that this library
*has no LOGGER implemented. Instead, this library has judiciously DELEGATED
*the logging responsibilty to the CONSUMER of this library.
*/
printingManiacConsumerLoggerHook("Actual Work", "Step " + iterator.ToString());
}
}
}
}
/*
* Let us assume that I have purchased the DoSomethingLibrary DLL from a vendor.
* I have to add the DLL as a reference to my executable's project in Visual Studio.
* I also have to use the DoSomethingLibrary namespace to access the Logic in the DLL.
*/
using DoSomethingLibrary;
namespace UnderstandingDelegates
{
class Program
{
static void Main(string[] args)
{
/*
* Creating an object of the lone class PrintingManiac in the DoSomethingLibrary
*/
PrintingManiac newManiac = new PrintingManiac();
/*
* HOOKING my custom logger to the DoSomethingLibrary DLL.
* I get the best of both the worlds. I have a well-tested and efficient library working for me
* AND I have the best logging avaliable.
* The DoSomethingLibrary DLL has no knowledge of what logging this executable is going to use.
* This executable has to just satisfy the requirements of the DELEGATE signature of DoSomethingLibrary DLL.
*/
newManiac.printingManiacConsumerLoggerHook += new Logger(ClientsCustomizedLoggerTwo);
newManiac.StartPrintingLikeAManiac();
Console.ReadLine();
}
public static void ClientsCustomizedLoggerOne(string firstParam, string secondParam)
{
/*
*This logger has '=' used as a decorator
*In real scenarios the logger may be very complex.
*Let us assume this is an HTML logger
*/
Console.WriteLine("=============================");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("=============================");
}
public static void ClientsCustomizedLoggerTwo(string firstParam, string secondParam)
{
/*
*This logger has '-' used as a decorator
*Let us assume this is an XML logger
*/
Console.WriteLine("------------------------------");
Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
Console.WriteLine("------------------------------");
}
}
}
我写了一篇文章来清楚地解释委托的整个概念。事件由两种特殊的方法组成,称为访问器,即
添加
和删除
。两者都接受同一委托类型的一个值参数value
,并返回void
例如,这是一个事件:
public event Action Exploded
{
add
{
Console.WriteLine("Hello from 'add'. Type of 'value' is '{0}'.", value);
}
remove
{
Console.WriteLine("Hello from 'remove'. Type of 'value' is '{0}'.", value);
}
}
“类似于字段”的事件是编译器生成的事件类型,其中存在相关委托类型的私有生成支持字段,并且
add
访问器将value
添加到该支持字段(委托组合)的调用列表中而remove
访问器从该列表中删除value
。这是以智能线程安全的方式完成的。事件由两个称为访问器的特殊方法组成,即add
和remove
。两者都接受同一委托类型的一个值参数value
,并返回void
例如,这是一个事件:
public event Action Exploded
{
add
{
Console.WriteLine("Hello from 'add'. Type of 'value' is '{0}'.", value);
}
remove
{
Console.WriteLine("Hello from 'remove'. Type of 'value' is '{0}'.", value);
}
}
“类似于字段”的事件是编译器生成的事件类型,其中存在相关委托类型的私有生成支持字段,并且add
访问器将value
添加到该支持字段(委托组合)的调用列表中而remove
访问器从该列表中删除value
。这是以智能线程安全的方式完成的
如何正确地将整个概念形象化
在C#中可视化属性从来没有人遇到过很多麻烦。属性是字段的访问器,它阻止其他代码直接操作字段值。这样的代码被迫调用get和set访问器来访问字段。您可以在访问器方法中放入任意代码,当您对传递给setter的值不满意时抛出异常,例如,非常常见。属性的底层存储也不必是字段,例如,您可以公开另一个类对象的字段或属性。等等
可视化事件的一个好方法是将其与属性进行比较。出于完全相同的目的,它阻止其他代码直接操作委托对象。他们必须通过添加和删除访问器。对这些方法的调用由客户端代码中的语法sugar生成,+=
操作符调用add(),-=
调用remove()。与用于访问属性的语法sugar类似,您也没有显式地编写对get或set方法的调用
使事件混淆并使它们看起来与属性如此不同的是,事件访问器方法是可选的。如果您不编写它们,C#编译器将自动为您生成它们。包括备份存储,一个委托对象。属性还可以具有自动生成的访问器和备份存储,而自动属性则可以。但是语法不同,您仍然需要声明它们
使用自动生成的访问器非常常见。这段代码几乎总是足够好的,它已经保证了任意代码不能删除其他代码的事件订阅。没有那么多好的理由写你自己的。一种是,如果您支持很多事件,那么可以减少类对象的大小。您可以将它们存储在EventHandlerList中,而不是为每个单独的事件使用委托对象。例如,在.NET framework代码中非常常见。WPF附加的事件中也利用了额外的间接性,WinRT的事件模型不基于委托
如何正确地将整个概念形象化
在C#中可视化属性从来没有人遇到过很多麻烦。属性是字段的访问器,它阻止其他代码直接操作字段值。这样的代码被迫调用get和set访问器来访问字段。您可以在访问器方法中放入任意代码,当您对传递给setter的值不满意时抛出异常,例如,非常常见。属性的底层存储也不必是字段,例如,您可以公开另一个类对象的字段或属性。等等
可视化事件的一个好方法是将其与属性进行比较。出于完全相同的目的,它阻止其他代码直接操作委托对象。他们必须通过添加和删除访问器。对这些方法的调用由客户端代码中的语法sugar生成,+=
操作符调用add(),-=
调用remove()。与用于访问属性的语法sugar类似,您也没有显式地编写对get或set方法的调用
使事件混淆并使它们看起来与属性如此不同的是,事件访问器方法是可选的。如果您不编写它们,那么C#编译