这是简化的案例.我有一个类来存储它将在完成时调用的委托:
public class Animation { public delegate void AnimationEnd(); public event AnimationEnd OnEnd; }
我有另一个实用工具类,我想订阅各种代表.在构造我想要自己注册代表,但除此之外它不关心类型.问题是,我不知道如何在类型系统中表达.这是我的伪C#
public class WaitForDelegate { public delegateFired = false; // How to express the generic type here? public WaitForDelegate<F that's a delegate>(F trigger) { trigger += () => { delegateFired = true; }; } }
提前致谢!
感谢Alberto Monteiro,我只使用System.Action作为活动的类型.我现在的问题是,如何将事件传递给构造函数以便它可以自己注册?这可能是一个非常愚蠢的问题.
public class Example { Animation animation; // assume initialized public void example() { // Here I can't pass the delegate,and get an error like // "The event can only appear on the left hand side of += or -=" WaitForDelegate waiter = new WaitForDelegate(animation.OnEnd); } }
解决方法
我担心你不能做你所要求的.
首先,你不能被代表约束.与合法C#最接近的代码是:
public class WaitForDelegate<F> where F : System.Delegate { public bool delegateFired = false; public WaitForDelegate(F trigger) { trigger += () => { delegateFired = true; }; } }
但它不会编译.
但更大的问题是,无论如何你都无法像这样传递代表.
考虑这个简化的类:
public class WaitForDelegate { public WaitForDelegate(Action trigger) { trigger += () => { Console.WriteLine("trigger"); }; } }
然后我尝试像这样使用它:
Action bar = () => Console.WriteLine("bar"); var wfd = new WaitForDelegate(bar); bar();
唯一的输出是:
bar
单词触发器不会出现.这是因为代理是按值复制的,因此行trigger =()=> {Console.WriteLine(“trigger”); };只是将处理程序附加到触发器而不是bar.
您可以完成所有这些工作的方法是停止使用事件并使用Microsoft的Reactive Extensions(NuGet“Rx-Main”),它允许您将事件转换为基于LINQ的IObservable< T>可以传递的实例.
以下是我上面的示例代码如何工作:
public class WaitForDelegate { public WaitForDelegate(IObservable<Unit> trigger) { trigger.Subscribe(_ => { Console.WriteLine("trigger"); }); } }
你现在称之为:
Action bar = () => Console.WriteLine("bar"); var wfd = new WaitForDelegate(Observable.FromEvent(h => bar += h,h => bar -= h)); bar();
这现在产生输出:
bar trigger
请注意,Observable.FromEvent调用包含用于在有权访问的作用域中附加和分离处理程序的代码.它允许通过调用.Dispose()来解除最终订阅调用.
我让这个课很简单,但更完整的版本是这样的:
public class WaitForDelegate : IDisposable { private IDisposable _subscription; public WaitForDelegate(IObservable<Unit> trigger) { _subscription = trigger.Subscribe(_ => { Console.WriteLine("trigger"); }); } public void Dispose() { _subscription.Dispose(); } }
如果您不想充分利用Rx,另一种方法是:
public class WaitForDelegate : IDisposable { private Action _detach; public WaitForDelegate(Action<Action> add,Action<Action> remove) { Action handler = () => Console.WriteLine("trigger"); _detach = () => remove(handler); add(handler); } public void Dispose() { if (_detach != null) { _detach(); _detach = null; } } }
你这样称呼它:
Action bar = () => Console.WriteLine("bar"); var wfd = new WaitForDelegate(h => bar += h,h => bar -= h); bar();
那仍然是正确的输出.