Я тоже хотел это сделать, и я пришел с довольно прохладным способом, что делает что-то вроде императора XLII идеи. Он не использует деревья выражений, хотя, как уже упоминалось, это невозможно сделать, поскольку деревья выражений не позволяют использовать +=
или -=
.
Однако мы можем использовать опрятный трюк, в котором мы используем прокси-сервер .NET Remoting Proxy (или любой другой прокси, такой как LinFu или Castle DP), чтобы перехватить вызов Add/Remove handler на очень коротком прокси-объекте. Роль этого прокси-объекта состоит в том, чтобы просто вызвать на него какой-то метод и разрешить перехватывать его вызовы методов, после чего мы можем узнать название события.
Это звучит странно, но вот код (который, кстати, работает только если у вас есть MarshalByRefObject
или интерфейс для объекта прокси)
Предположим, мы имеем следующий интерфейс и класс
public interface ISomeClassWithEvent {
event EventHandler<EventArgs> Changed;
}
public class SomeClassWithEvent : ISomeClassWithEvent {
public event EventHandler<EventArgs> Changed;
protected virtual void OnChanged(EventArgs e) {
if (Changed != null)
Changed(this, e);
}
}
Тогда у нас может быть очень простой класс, который ожидает делегата Action<T>
, который получит какой-то экземпляр T
.
Вот код
public class EventWatcher<T> {
public void WatchEvent(Action<T> eventToWatch) {
CustomProxy<T> proxy = new CustomProxy<T>(InvocationType.Event);
T tester = (T) proxy.GetTransparentProxy();
eventToWatch(tester);
Console.WriteLine(string.Format("Event to watch = {0}", proxy.Invocations.First()));
}
}
Хитрость заключается в том, чтобы пройти через прокси-объект в Action<T>
делегата.
Где мы имеем следующий CustomProxy<T>
код, который перехватывает вызов +=
и -=
на проксируемом объекте
public enum InvocationType { Event }
public class CustomProxy<T> : RealProxy {
private List<string> invocations = new List<string>();
private InvocationType invocationType;
public CustomProxy(InvocationType invocationType) : base(typeof(T)) {
this.invocations = new List<string>();
this.invocationType = invocationType;
}
public List<string> Invocations {
get {
return invocations;
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
[DebuggerStepThrough]
public override IMessage Invoke(IMessage msg) {
String methodName = (String) msg.Properties["__MethodName"];
Type[] parameterTypes = (Type[]) msg.Properties["__MethodSignature"];
MethodBase method = typeof(T).GetMethod(methodName, parameterTypes);
switch (invocationType) {
case InvocationType.Event:
invocations.Add(ReplaceAddRemovePrefixes(method.Name));
break;
// You could deal with other cases here if needed
}
IMethodCallMessage message = msg as IMethodCallMessage;
Object response = null;
ReturnMessage responseMessage = new ReturnMessage(response, null, 0, null, message);
return responseMessage;
}
private string ReplaceAddRemovePrefixes(string method) {
if (method.Contains("add_"))
return method.Replace("add_","");
if (method.Contains("remove_"))
return method.Replace("remove_","");
return method;
}
}
И тогда мы все, что осталось, чтобы использовать эту функцию следующим образом
class Program {
static void Main(string[] args) {
EventWatcher<ISomeClassWithEvent> eventWatcher = new EventWatcher<ISomeClassWithEvent>();
eventWatcher.WatchEvent(x => x.Changed += null);
eventWatcher.WatchEvent(x => x.Changed -= null);
Console.ReadLine();
}
}
При этом я увижу этот выход:
Event to watch = Changed
Event to watch = Changed