似乎无法通过引用传递 EventArgs

Can't seem to Pass EventArgs By Reference

我有一个引发事件的 class。我希望订阅者能够修改在 EventArgs 中传递的值。

在引发事件的 class 中:

    class Factory
    {
    public event EventHandler<MessageReceivedEventArgs> MessageReceived;

    private IServerLib _myObject;

    public void Connect()
    {
        _myObject = new ServerLib();
        _myObject.AddMessageReceivedHandler((short terminal, ref string message, ref short functionNo) =>
        {
            MessageReceivedEventArgs args = new MessageReceivedEventArgs { Terminal = terminal, Message = message, FunctionNo = functionNo };
            MessageReceivedEvent(ref args);
        });
    }

    private void MessageReceivedEvent(ref MessageReceivedEventArgs args)
    {
        EventHandler<MessageReceivedEventArgs> handler = MessageReceived;
        if (handler != null)
        {
            handler(this, args);
        }
    }

    public class MessageReceivedEventArgs : EventArgs
    {
        public short Terminal { get; set; }
        public string Message { get; set; }
        public short FunctionNo { get; set; }
    }
}


interface IServerLib
    {
        void AddMessageReceivedHandler(MessageReceivedEventHandler action);
    }
    public delegate void MessageReceivedEventHandler(short terminal, ref string message, ref short functionNo);

订户(恰好是 VB)如下所示:

Dim WithEvents _va As MyAssembly.MyClass

Private Sub _va_MessageReceived(sender As Object, e As Factory.MessageReceivedEventArgs) Handles _va.MessageReceived
    Debug.WriteLine($"Message: {e.Message} Terminal: {e.Terminal} Function: {e.FunctionNo}")
    If e.Message = "1" Then
        e.Message = ""
        e.FunctionNo = 0
        Debug.WriteLine("Cancelled")
    End If
End Sub

这引发了事件,但设置 e.Message 和 e.Function 似乎没有设置值。我做错了什么吗?

您似乎认为您的 "ref" 参数应该受到上述代码的影响,但事实并非如此。是的,您通过引用 MessageReceivedEvent 函数传递 message,但随后将其分配给 MessageReceivedEventArgs.Message,这是按值而不是引用发生的。

结果,当您在 VB 代码中修改 MessageReceivedEventArgs.Message - message 变量不受影响(但 MessageReceivedEventArgs.Message 当然会受到影响),尽管你通过引用传递了它,因为它应该是。

您应该做的是直接将 MessageReceivedEventArgs 的实例传递给您的函数(而不是在该函数内部创建它):

private void MessageReceivedEvent(MessageReceivedEventArgs args)
{
    EventHandler<MessageReceivedEventArgs> handler = MessageReceived;
    if (handler != null)
    {
        handler(this, args);
    }
}

问题出在这一行的使用上:

var args = new MessageReceivedEventArgs
           { Terminal = terminal, Message = message, FunctionNo = functionNo };

它将所有变量复制到事件参数 class。在那里更改它不会自动在另一端更改它(您添加 ref 的地方)。这不是一个很好的解决方案,但为了向您证明这是问题所在,请在 handler(this, args):

之后添加
message = args.Message;
functionNo = args.FunctionNo;

这将导致 ref 覆盖值。

首先,您不需要 ref 来更改 MessageReceivedEventArgs 的 属性。对象变量只是指向内存中真实对象的指针,因此您已经更改了它的属性。如果您需要您的订阅者更改引用本身——即将指针重置为另一个对象——那么您需要使用 ref。 其次,这个设计很糟糕,因此我不确定我是否理解这个问题。无论如何,我将提供一种正确的方法来引发事件并在之后使用更改的值。

class Factory 
{
    public event EventHandler<MessagereceivedEventArgs> MessageReceived;

    void ReceiveMessage(string Message)
    {
        // Do something with the Message

        // Then let your subscribers know that the message has been processed:
        if (MessageReceived != null)
        {
            var ea = new MessageReceivedEventArgs();
            ea.Message = Message;
            // Set ea properties as appropriate 
            MessageReceived(this, ea);
            // Check ea properties for change
            if (ea.Message != Message)
            {
                // A subscriber has changed the message in the MessageReceivedEventArgs
            }
        }
    }
}