[C#] リフレクションを使って外部からイベントを発生させる

結論から書くと、リフレクションでイベントを発生させる汎用的な方法はない。
対象のコードを読んで、適当な方法を探すことになる。

## 発生させたいイベントにadd/removeアクセサが自動で定義されている場合

発生させたいイベントが

```csharp
public event EventHandler ValueChanged;
```

のように、add/removeがコードにない場合は、イベントと同名のprivateフィールドを取得することで、イベントを発生させることができる

```csharp
public static void RaiseEvent<TEventArgs>(
    object target,
    string eventName,
    TEventArgs eventArg)
{
    var type = target.GetType();
    var field = type.GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
    var handler = (MulticastDelegate)field.GetValue(target);

    if (handler is null) return;

    foreach (var h in handler.GetInvocationList())
        h.Method.Invoke(h.Target, new object[] { target, eventArg });
}
```

使い方

```csharp
// AはValueChangedイベントを持ち、add/removeアクセサを定義していないものとする
var a = new A();
a.ValueChanged += (sender, e) => System.Diagnostics.Trace.WriteLine("Raised");
RaiseEvent(a, "ValueChanged", EventArgs.Empty);
```

## 発生させたいイベントにadd/removeアクセサが定義されており、内部でEventHandlerListを使用している場合

```csharp
public event EventHandler Click
{
    add
    {
        Events.AddHandler(EventClick, value);
    }
    remove
    {
        ...
    }
}
```

のようになっており、`Events`が`EventHandlerList`型のプロパティである場合。
`EventHandlerList`は複数のイベントに対し、デリゲートを管理することができる。
そして、イベントの識別用になんらかのobjectを指定することになっており、ここでは、Clickイベント用識別オブジェクトとして、`EventClick`が使用されている。

- `EventHandlerList`型のプロパティの名前
- イベントの識別用のオブジェクト

があれば、イベントを発生させることができる。
WinFormsのControlではこの手が使用できる。

```csharp
public static void RaiseEvent<TEventHandler, TEventArgs>(
    object target,
    object eventKeyObject,
    string eventHandlerListPropertyName,
    TEventArgs eventArgs)
    where TEventHalder : Delegate
{
    var type = target.GetType();
    var property = type.GetProperty(eventHandlerListPropertyName, BindingFlags.Instance | BindingFlags.NonPublic);
    var list = property.GetValue(target) as System.ComponentModel.EventHandlerList;
    var eventDelegate = list[eventKeyObject] as TEventHalder;

    eventDelegate?.DynamicInvoke(new[] { target, eventArgs });
}

public static object GetStaticNonPublicField(
    Type target,
    string fieldName)
{
    return target.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic).GetValue(target);
}
```

使い方

```csharp
using (var label = new Label())
{
    label.Click += (sender, e) => System.Diagnostics.Trace.WriteLine("Raised");

    var key = GetStaticNonPublicField(typeof(Control), "EventClick");
    RaiseEvent<EventHandler, EventArgs>(label, key, "Events", EventArgs.Empty);
}
```

イベントを発生させたいクラスは`Label`であるが、そのキーとなるオブジェクト`EventClick`は`Control`クラスにprivateで定義されているので、オブジェクトの取得時に`typeof(Control)`とする必要がある。

## 「Onイベント名」のメソッドを呼べばいいと割り切った場合

```csharp
// ValueChangedはEventHandler型のイベント
protected void OnValueChanged(EventArgs e) => ValueChanged?.Invoke(this, e);
```

こういうメソッドが存在する場合は、単純にこのメソッドを呼べばよい。
イベントに対してこうしたメソッドを用意しておくことが.NETでは推奨されているので、かなり広範囲で使用できるはず。

```csharp
public static void ExecuteOnMethod<TEventArgs>(
    object target,
    string methodName,          // e.g. "OnEventName"
    TEventArgs eventArgs)
{
    var type = target.GetType();
    var method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);

    method.Invoke(target, new[] { eventArgs });
}
```

使い方

```csharp
// AはOnValueChangedメソッドを持ち、その中でValueChangedイベントを発生させる必要がある
var a = new A();
a.ValueChanged += (sender, e) => System.Diagnostics.Trace.WriteLine("Raised");
ExecuteOnMethod(a, "OnValueChanged", EventArgs.Empty);
```

## 参考にした情報

EventInfoを使ってイベントの発行を行う
https://smdn.jp/programming/dotnet-samplecodes/reflection/3a87ea19024111eb907175842ffbe222/

方法: イベント プロパティを使用して複数のイベントを処理する
https://learn.microsoft.com/ja-jp/dotnet/standard/events/how-to-handle-multiple-events-using-event-properties?redirectedfrom=MSDN