C# 关于C语言中的事件和委托#

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

将事件视为代理类型的实例有什么副作用

Jon Skeet说,“事件不是委托实例。”,。如果我在其他地方读过这篇文章,我就不会问这个问题

在过去的两个月里,我一直使用event关键字将事件可视化为特殊类型的委托,这有助于防止通过事件调用的委托无效

有没有人能详细说明一下,对于C#和基于事件的编程新手来说,如何正确地将整个概念形象化

编辑1:

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#编译