操作说明:使用字典存储事件实例(C# 编程指南)How to: use a dictionary to store event instances (C# Programming Guide)

addremove 自定义事件访问器的一个用途是,公开多个事件,但不为每个事件分配字段,而是改用 Dictionary<TKey,TValue> 实例来存储事件实例,如下面的示例所示。One use for the add and remove custom event accessors is to expose many events without allocating a field for each event, but instead using a Dictionary<TKey,TValue> instance to store the event instances, as the example below demonstrates. 这仅在一个类型有多个事件,但预计不会订阅大多数事件时有用。This is only useful if a type has many events, but you expect that most of the events will not be subscribed to.

using System;
using System.Collections.Generic;

public delegate void EventHandler1(int i);
public delegate void EventHandler2(string s);

public class PropertyEventsSample
{
    private const string Event1Key = nameof(Event1);
    private const string Event2Key = nameof(Event2);

    private readonly Dictionary<string, Delegate> handlers;

    public PropertyEventsSample()
    {
        handlers = new Dictionary<string, Delegate>
        {
            [Event1Key] = null,
            [Event2Key] = null
        };
    }

    public event EventHandler1 Event1
    {
        add
        {
            lock (handlers)
            {
                handlers[Event1Key] = (EventHandler1)handlers[Event1Key] + value;
            }
        }
        remove
        {
            lock (handlers)
            {
                handlers[Event1Key] = (EventHandler1)handlers[Event1Key] - value;
            }
        }
    }

    public event EventHandler2 Event2
    {
        add
        {
            lock (handlers)
            {
                handlers[Event2Key] = (EventHandler2)handlers[Event2Key] + value;
            }
        }
        remove
        {
            lock (handlers)
            {
                handlers[Event2Key] = (EventHandler2)handlers[Event2Key] - value;
            }
        }
    }

    internal void RaiseEvent1(int i)
    {
        EventHandler1 handler;
        lock (handlers)
        {
            handler = (EventHandler1)handlers[Event1Key];
        }
        handler?.Invoke(i);
    }

    internal void RaiseEvent2(string s)
    {
        EventHandler2 handler;
        lock (handlers)
        {
            handler = (EventHandler2)handlers[Event2Key];
        }
        handler?.Invoke(s);
    }
}

public static class TestClass
{
    private static void Delegate1Method(int i) => Console.WriteLine(i);

    private static void Delegate2Method(string s) => Console.WriteLine(s);

    private static void Main()
    {
        var p = new PropertyEventsSample();

        p.Event1 += Delegate1Method;
        p.Event1 += Delegate1Method;
        p.Event1 -= Delegate1Method;
        p.RaiseEvent1(2);

        p.Event2 += Delegate2Method;
        p.Event2 += Delegate2Method;
        p.Event2 -= Delegate2Method;
        p.RaiseEvent2("TestString");

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    2
    TestString
*/

此实现符合 C# 语言规范中的添加和删除委托行为。This implementation conforms to the behavior for adding and removing delegates in the C# language specification.

请注意,lock 语句仅用于通过事件处理程序访问字典。Note that the lock statement is used only to access the dictionary with event handlers. 不要在 lock 语句的主体内调用事件处理程序,因为它可能会导致死锁或锁争用。Don't invoke an event handler inside the body of the lock statement, as it could lead to deadlocks or lock contention.

请参阅See also