Я тоже хотел сделать это, и я придумал довольно крутой способ, который делает что-то вроде идеи Emperor XLII. Он не использует деревья выражений, хотя, как уже упоминалось, это невозможно сделать, поскольку деревья выражений не позволяют использовать +=
или -=
.
Однако мы можем использовать хитрый прием, когда мы используем .NET Remoting Proxy (или любой другой прокси, такой как LinFu или Castle DP), чтобы перехватить вызов обработчика Add / Remove для очень недолговечного прокси-объекта. Роль этого прокси-объекта состоит в том, чтобы просто вызывать некоторый метод и разрешать перехват вызовов его методов, после чего мы можем узнать имя события.
Звучит странно, но вот код (который, кстати, работает ТОЛЬКО, если у вас есть 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