【问题标题】:Weird execution for subscribers of an event in C#C#中事件订阅者的奇怪执行
【发布时间】:2014-03-27 07:35:30
【问题描述】:

我有一个事件及其方法声明如下,它是一个 Windows 窗体登录控件的身份验证事件:

public event EventHandler<AuthenticateEventArgs> Authenticate;

protected void OnAuthenticate(AuthenticateEventArgs e)
{
    EventHandler<AuthenticateEventArgs> handler = Authenticate;
    if (handler != null)
    {
        handler(this, e);
    }

    if (e.Authenticated)
    {
        OnLoggedIn(new EventArgs());
    }
        else
    {
        OnLoggedError(new EventArgs());
    }
}

点击按钮时会引发该事件,现在假设在其他一些项目中有该事件的订阅者,如下所示:

this.loginControl1.Authenticate += loginControl1_Authenticate;
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
        {
            System.Threading.Thread.Sleep(2000);
            ea.Authenticated = false;
        };
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
        {
            System.Threading.Thread.Sleep(2000);
            ea.Authenticated = true;
        };
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
        {
            System.Threading.Thread.Sleep(2000);
            ea.Authenticated = false;
        };

System.Threading.Thread.Sleep(2000); 只是对需要一些时间的某些过程的模拟。问题是最后一个订阅者在 OnAuthenticate 方法中执行 If 条件并引发另一个事件,而之前的订阅者没有。代码完美适用于一位订阅者。这种情况下问题出在哪里?

【问题讨论】:

  • 会发生什么?总是Authenticated 是假的?
  • 是的。 Authenticated 值将始终是最后一个订阅者中的值,无论它是 true 还是 false。
  • 您希望OnLoggedIn() 函数执行一次,OnLoggedError 执行两次,用给定的例子?这就是你想要的吗?
  • 是的,这就是我想要的。
  • OnLoggedIn()OnLoggedError() 的参数是什么。你需要AuthenticationArgs吗?

标签: c# events user-controls delegates event-handling


【解决方案1】:

由于您希望异步执行身份验证方法,您可以这样工作。

创建一个返回bool的委托

public delegate bool Authenticate(object sender, AuthenticateEventArgs e);

Authenticate authHandler;

您可以使用也可以不使用这些参数,但您可以稍后使用或删除它。

创建您的身份验证方法

bool AuthenticationMethod1(object o, AuthenticateEventArgs ea)
{
    System.Threading.Thread.Sleep(2000); //Simulate some long running task.
    return false; //Return true or false based on authentication failed or succeeded.
}

bool AuthenticationMethod2(object o, AuthenticateEventArgs ea)
{
    System.Threading.Thread.Sleep(2000); //Simulate some long running task.
    return true; //Return true or false based on authentication failed or succeeded.
}

bool AuthenticationMethod3(object o, AuthenticateEventArgs ea)
{
    System.Threading.Thread.Sleep(2000); //Simulate some long running task.
    return false; //Return true or false based on authentication failed or succeeded.
}

连接处理程序

authHandler += AuthenticationMethod1;
authHandler += AuthenticationMethod2;
authHandler += AuthenticationMethod3;

现在执行

if (authHandler != null)
{
    foreach (Authenticate handler in authHandler.GetInvocationList())
    {
        handler.BeginInvoke(this, e as AuthenticateEventArgs, new AsyncCallback(Callback), handler);
    }                
}

最后一部分:你定义了回调吗

void Callback(IAsyncResult ar)
{
    Authenticate d = (Authenticate)ar.AsyncState;
    if (d.EndInvoke(ar))
    {
       OnLoggedIn(new EventArgs());
    }
    else
    {
       OnLoggedError(new EventArgs());
    }
}

【讨论】:

  • 我将尝试代码并查看结果。感谢您的帮助:)
  • 完美!这就是我要找的东西!
  • 使用您的代码,我认为我不需要自定义 EventArgs 类。我会试试的。感谢大佬的帮助! :)
【解决方案2】:

通过此代码删除事件:

this.loginControl1.Authenticate -= loginControl1_Authenticate;

【讨论】:

  • 没有解决。最后一个订阅者仍然只评估 if 并引发其他事件。
【解决方案3】:

问题是所有事件订阅者将在与触发它们的函数相同的线程上执行,所以当执行到达时

        handler(this, e);

执行将移至第一个订阅者代码

        System.Threading.Thread.Sleep(2000);
        ea.Authenticated = false;

然后执行转到第二个订阅者。最后它会执行最后一个订阅者代码,然后执行回到调用者函数

protected void OnAuthenticate(AuthenticateEventArgs e)

从事件触发行继续执行到第二个 if 语句,此时 ea.Authenticated 的值为 false,因为最后一个订阅者设置了它。

如果您想在不同的线程上引发每个事件,请检查 Trigger events on separated threads

【讨论】:

    【解决方案4】:

    这是众所周知的行为,事件按订阅顺序执行,因此最后订阅的订阅者将覆盖之前的所有值。您将看到最后一个订阅者的更新(在本例中为 false)。

    您可以通过检查已验证并跳过处理来解决此问题。

    this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
    {
        if(ea.Authenticated)
        {
            return;
        }
        System.Threading.Thread.Sleep(2000);
        ea.Authenticated = false;
    };
    this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
    {        
        if(ea.Authenticated)
        {
            return;
        }
        System.Threading.Thread.Sleep(2000);
        ea.Authenticated = true;
    };
    this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
    {
        if(ea.Authenticated)
        {
            return;
        }
        System.Threading.Thread.Sleep(2000);
        ea.Authenticated = false;
    };
    

    原因是因为你看到 Authenticated false 总是 handler(this, e); 只会在调用所有订阅的方法后返回。因此,您的最后一个订阅者将 Authenticated 设置为 false,因此只有在执行 if (e.Authenticated) 时您才会看到 false。

    【讨论】:

    • 覆盖不是我的问题,但是为什么以前的订阅者不会评估if并引发另一个事件?我希望每个订阅者都评估 if 并基于它引发其他事件。这段代码可能看起来不合逻辑,但这只是我在其中搜索的一种特殊情况。
    • 你的意思是你希望OnLoggedIn 被解雇,对吧?请试试这个,这肯定会解决你的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多