特定のイベントを介してLinqツリー表現
-
09-06-2019 - |
質問
のコンパイラは通常chokesイベントが表示されていないそば +=
または -=
, なんだことが可能です。
こういうことができるように特定のイベントを表すツリーだったので、イベントを生成する観のためのテストです。の構文を見て思ったこと。
using(var foo = new EventWatcher(target, x => x.MyEventToWatch) {
// act here
} // throws on Dispose() if MyEventToWatch hasn't fired
私の質問は二
- のコンパイラー-チョーク?ている場合はその提案などを防止す。
- たいのですが構文解析表現からオブジェクトのコンストラクタのために取り付け
MyEventToWatch
イベントtarget
?
解決
編集: として Curt 指摘のように、私の実施は不良でのみ使用できますから、クラスを宣言するイベント)の代わりに"x => x.MyEvent
"のイベントでのバッキング分野におけaccessbleのクラスです。
以来、表現を含むことができ課題諸表修正の表現"( x, h ) => x.MyEvent += h
"は使用できませんを取得するイベントで反射することができるようにしております。正しい実装を利用する必要があり反射を取得する EventInfo
のイベント(unfortunatley、強く型付けされた).
そうしないと、だけるアップデートする必要があるが、反映され 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_ );
}
}
使い方は若干異なることを示唆し、活用するための型推論:
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 );
}
他のヒント
私もやりたいことって良いものになる方法ではそのような天皇XLII。のご使用はしないでください表現の樹木もど述べたように、このメソッドはできないので表現としての樹木が使用できないの +=
または -=
.
また使用のコントします。純リモプロキシ(又はその他のプロキシがリクエストなどのLinFuや城DP)相手の呼び出しの追加と削除ハンドラは非常に短寿命のプロキシオブジェクトです。これらのプロキシオブジェクトであることができるようになるか、そのメソッドの呼び出しを遮断する、またの名前のイベントです。
こんなこちらのコードするようにだけ動作した場合 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
A.ネイベントなオブジェクトでのエンドポイントに代表される二つの機能を一つの追加と削除ハンドラです。だからこそコンパイラさせない以外のもの+=る、追加または-=(の削除).
のみが参照できるようになイベントのためのmetaprogramming目的としています。反射を生み出します。EventInfo、反射はその最良の方法(いない場合のみ)を取得しahold。
編集:天皇XLIIはいくつかの美しいコードはご自身のイベントは、まっ宣言されてからのC#としてとらえ、
public event DelegateType EventName;
これはC#の作成に二つのことをする宣言:
- 民間委譲分野として、バッキング 保存のためのイベント
- 実際のイベントと共に 実装コードを利用し のに限ります。
便利に、これらと同じ名前です。このような理由から、サンプルコードまご自身のイベント。
ただし、できなことに利用時のイベントが実施する。特に、このイベントをWindows Formsおよびコンポーネントのラインナップにない独自のバックで保管でのサンプルコードではないられるようになりました。
が天皇XLIIていたの答えはこれしかったで価値のあるものでなければならな自分の書き換えます。しかし残念ないかは表現のツリーを使用している名前のイベントです。
public sealed class EventWatcher : IDisposable {
private readonly object _target;
private readonly EventInfo _eventInfo;
private readonly Delegate _listener;
private bool _eventWasRaised;
public static EventWatcher Create<T>(T target, string eventName) {
EventInfo eventInfo = typeof(T).GetEvent(eventName);
if (eventInfo == null)
throw new ArgumentException("Event was not found.", eventName);
return new EventWatcher(target, eventInfo);
}
private EventWatcher(object target, EventInfo eventInfo) {
_target = target;
_eventInfo = event;
_listener = CreateEventDelegateForType(_eventInfo.EventHandlerType);
_eventInfo.AddEventHandler(_target, _listener);
}
// SetEventWasRaised()
// CreateEventDelegateForType
void IDisposable.Dispose() {
_eventInfo.RemoveEventHandler(_target, _listener);
if (!_eventWasRaised)
throw new InvalidOperationException("event was not raised.");
}
}
と使い方は:
using(EventWatcher.Create(o, "MyEvent")) {
o.RaiseEvent();
}