移除匿名事件处理程序

26

我有以下代码,其中SprintServiceClient是对一个WCF服务的引用 -

public class OnlineService
{
    private SprintServiceClient _client;
    public OnlineService()
    {
        _client = new SprintServiceClient();
    }

    public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
    {
        _client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
        _client.AddMemberToTeamAsync(user.ToUser(), projectId);
    }
}
问题在于每次调用AddMemberToTeam时,它都会向client.AddMemberToTeamCompleted添加另一个回调函数。也就是说,第一次调用AddMemberToTeam时,回调函数被调用一次,第二次调用AddMemberToTeam时,回调函数被调用两次,以此类推。
是否有办法在事件处理程序被调用后从AddMemberToTeamCompleted中删除事件处理程序,或者使用另一种接受回调函数的方法?

要删除它,您需要一个引用。假设“AddMemberToTeamCompleted”使用“event”关键字定义(仅剩下-=和+=运算符),则必须维护具有委托实例的变量。第一次创建并添加它,不要再添加任何内容,关闭时再删除。 - Marvin Smit
3个回答

53

只要先将委托分配给一个变量,你就可以在匿名方法内部引用它自己:

EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
    {
        _client.AddMemberToTeamCompleted -= handler;
        callback(e.Result);
    };

_client.AddMemberToTeamCompleted += handler;

请注意,在方法体内使用变量之前,您需要声明变量并单独赋值,否则编译器将视其为未初始化。


19

制作一个自我取消订阅的事件处理程序的技巧是捕获 处理程序本身,以便您可以在-=中使用它。但是有一个声明确定分配的问题; 因此我们不能像这样做:

EventHandler handler = (s, e) => {
    callback(e.Result);
    _client.AddMemberToTeamCompleted -= handler; // <===== not yet defined     
};
所以我们将其初始化为null,这样声明就在使用之前,并且在第一次使用之前它具有已知值(null)。
EventHandler handler = null;
handler = (s, e) => {
    callback(e.Result);
    _client.AddMemberToTeamCompleted -= handler;        
};
_client.AddMemberToTeamCompleted += handler;

我好像有些地方没懂...请问可以有人解释一下为什么在创建代理到匿名方法时捕获handler的值却不是null?难道赋值运算符的右侧先被处理吗?所以,handler的值不应该在赋值给新的匿名代理之前就被捕获了吗?然而,这段代码似乎能够正常工作,所以我很好奇为什么它能够成功,而不是试图去除空的handler。是因为匿名方法中外部变量的值捕获发生在handler的赋值之后吗? - reirab
1
在C#中,被捕获的是变量本身,而不是当前值。 - Marc Gravell
啊,好的,谢谢。我知道C#编译器实际上创建了一个包括匿名方法和被捕获变量的新对象,但是我之前没有意识到新对象中的变量实际上是外部类使用的变量,而不仅仅是它的一份副本。有趣。 - reirab

2

没有办法。

显然,Tim和Marc有另一个好的解决方案。

但你总是可以给它们命名,在这个方法中对命名的事件处理程序执行-=操作 ;)

猜测你的事件:

_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;

并且

public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
    _client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
    callback(e.Result)
}

下一个问题是在您的监听器中获取此回调。也许将其放在EventArgs的属性中(但那感觉有点不干净,我同意)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接