2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-02-21

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

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

発生させたいイベントが

public event EventHandler ValueChanged;

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

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 });
}

使い方

// 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を使用している場合

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

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

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

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

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);
}

使い方

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

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

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

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

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

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

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 });
}

使い方

// 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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?