0

イベントをデリゲート型のインスタンスと見なすことの副作用は何ですか?

Jon Skeet は、「イベントはデリゲート インスタンスではありません」と述べています. もし私がこれを他の場所で読んでいたら、私はこれを尋ねなかったでしょう。

過去 2 か月間、私は常にイベントを特別なタイプのデリゲートとして視覚化し、イベントキーワードを使用して、イベントを介して呼び出されるデリゲートの無効化を防止していました。

C# やイベント ベースのプログラミングが初めての人のために、コンセプト全体を適切に視覚化する方法を詳しく説明してもらえますか?

編集1:

デリゲートの概念を理解した結果、イベントは非常に単純な概念であることがわかりました。これらの構造物との戦いの中で私が思いついた例を追加したいと思います。より理解を深めるために、多くのコメントを追加しました。これは、私のような初心者向けです。

ライブラリ 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:

デリゲートの概念全体を明確に説明するために、CodeProject に記事を書きました。

4

3 に答える 3

3

コンセプト全体を適切に視覚化する方法

C# でプロパティを視覚化するのに苦労した人はいません。プロパティはフィールドへのアクセサーであり、他のコードがフィールド値を直接操作するのを防ぎます。このようなコードは、フィールドにアクセスするためにgetおよびsetアクセサーを呼び出す必要があります。アクセサー メソッドに任意のコードを配置して、setter に渡された値に満足できない場合 (非常に一般的など) に例外をスローすることができます。プロパティの基になるストレージもフィールドである必要はありません。たとえば、別のクラス オブジェクトのフィールドまたはプロパティを公開できます。など。

イベントを視覚化する良い方法は、プロパティと比較することです。まったく同じ意図で、他のコードがデリゲート オブジェクトを直接操作するのを防ぎます。addおよびremoveアクセサーを通過する必要があります。これらのメソッドへの呼び出しは、クライアント コードの構文シュガーによって生成され、+=オペレーターは add() を-=呼び出し、remove() を呼び出します。プロパティにアクセスするためのシュガー構文と同様に、get または set メソッドへの呼び出しも明示的に記述しません。

イベントについて混乱を招き、プロパティと大きく異なるように見えるのは、イベント アクセサー メソッドがオプションであることです。それらを記述しない場合は、C# コンパイラによって自動生成されます。バッキング ストア、デリゲート オブジェクトを含みます。プロパティは、自動生成されたアクセサーとバッキング ストアを持つこともできます。自動プロパティはそうです。ただし、構文は異なります。宣言する必要があります。

自動生成されたアクセサーを使用することは非常に一般的です。そのコードは、ほとんどの場合、十分であり、任意のコードが他のコードのイベント サブスクリプションを削除できないという保証を既に提供しています。自分で書く正当な理由はそれほど多くありません。1 つは、多くのイベントをサポートする場合、クラス オブジェクトのサイズを削減することです。個々のイベントごとにデリゲート オブジェクトを用意する代わりに、それらを EventHandlerList に格納できます。たとえば、.NET フレームワーク コードでは非常に一般的です。追加の間接化は、WPF の添付イベントでも利用され、WinRT のイベント モデルはデリゲートに基づいていません。

于 2013-08-10T15:16:58.517 に答える