当一个事件没有出现在a +=或a 旁边时,编译器通常会窒息-=,所以我不确定这是否可行.
我希望能够通过使用表达式树来识别事件,因此我可以为测试创建事件监视器.语法看起来像这样:
using(var foo = new EventWatcher(target, x => x.MyEventToWatch) {
// act here
} // throws on Dispose() if MyEventToWatch hasn't fired
Run Code Online (Sandbox Code Playgroud)
我的问题有两个:
MyEventToWatch事件target?编辑:正如Curt所指出的,我的实现是相当有缺陷的,因为它只能在声明事件的类中使用:)而不是“ x => x.MyEvent”返回事件,而是返回支持字段,该字段只能通过班上。
由于表达式不能包含赋值语句,因此不能使用像“”这样的修改表达式( x, h ) => x.MyEvent += h来检索事件,因此需要使用反射来代替。正确的实现需要使用反射来检索EventInfo事件(不幸的是,这不会是强类型的)。
否则,唯一需要进行的更新是存储反射的EventInfo,并使用AddEventHandler/RemoveEventHandler方法注册侦听器(而不是手动Delegate Combine/Remove调用和字段集)。其余的实现不需要改变。祝你好运 :)
注意:这是演示质量的代码,对访问器的格式做出了一些假设。适当的错误检查、静态事件处理等留给读者作为练习;)
public sealed class EventWatcher : IDisposable {
private readonly object target_;
private readonly string eventName_;
private readonly FieldInfo eventField_;
private readonly Delegate listener_;
private bool eventWasRaised_;
public static EventWatcher Create<T>( T target, Expression<Func<T,Delegate>> accessor ) {
return new EventWatcher( target, accessor );
}
private EventWatcher( object target, LambdaExpression accessor ) {
this.target_ = target;
// Retrieve event definition from expression.
var eventAccessor = accessor.Body as MemberExpression;
this.eventField_ = eventAccessor.Member as FieldInfo;
this.eventName_ = this.eventField_.Name;
// Create our event listener and add it to the declaring object's event field.
this.listener_ = CreateEventListenerDelegate( this.eventField_.FieldType );
var currentEventList = this.eventField_.GetValue( this.target_ ) as Delegate;
var newEventList = Delegate.Combine( currentEventList, this.listener_ );
this.eventField_.SetValue( this.target_, newEventList );
}
public void SetEventWasRaised( ) {
this.eventWasRaised_ = true;
}
private Delegate CreateEventListenerDelegate( Type eventType ) {
// Create the event listener's body, setting the 'eventWasRaised_' field.
var setMethod = typeof( EventWatcher ).GetMethod( "SetEventWasRaised" );
var body = Expression.Call( Expression.Constant( this ), setMethod );
// Get the event delegate's parameters from its 'Invoke' method.
var invokeMethod = eventType.GetMethod( "Invoke" );
var parameters = invokeMethod.GetParameters( )
.Select( ( p ) => Expression.Parameter( p.ParameterType, p.Name ) );
// Create the listener.
var listener = Expression.Lambda( eventType, body, parameters );
return listener.Compile( );
}
void IDisposable.Dispose( ) {
// Remove the event listener.
var currentEventList = this.eventField_.GetValue( this.target_ ) as Delegate;
var newEventList = Delegate.Remove( currentEventList, this.listener_ );
this.eventField_.SetValue( this.target_, newEventList );
// Ensure event was raised.
if( !this.eventWasRaised_ )
throw new InvalidOperationException( "Event was not raised: " + this.eventName_ );
}
}
Run Code Online (Sandbox Code Playgroud)
为了利用类型推断,用法与建议的用法略有不同:
try {
using( EventWatcher.Create( o, x => x.MyEvent ) ) {
//o.RaiseEvent( ); // Uncomment for test to succeed.
}
Console.WriteLine( "Event raised successfully" );
}
catch( InvalidOperationException ex ) {
Console.WriteLine( ex.Message );
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2925 次 |
| 最近记录: |