似乎无法通过引用传递 EventArgs

3
我可以帮您进行翻译。以下是需要翻译的内容:

我有一个类,会触发一个事件。我希望订阅者能够修改传递给 EventArgs 的值。

在触发事件的类中:

    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”参数会受到影响,那么是的 - 您正在做错误的事情,它们不会受到影响。相反,将MessageReceivedEventArgs的实例传递给您的MessageReceivedEvent。 - Evk
@Evk - 我相信我正在执行 handler(this, args);,其中 args 是一个实例。 - Matt Wilko
你能展示一下真正可以编译的代码吗?(因为 (short x, ref string y, ref short z) => 不被允许,所以这段代码似乎有缺陷。) - Patrick Hofman
@PatrickHofman - 我会尝试,但我向您保证,该行是允许的,因为它在我的机器上编译通过。 - Matt Wilko
你能展示一下 AddMessageReceivedHandler 吗?它当然会获取一个委托... - Patrick Hofman
3个回答

3
问题出在使用了这行代码:
var args = new MessageReceivedEventArgs
           { Terminal = terminal, Message = message, FunctionNo = functionNo };

它将所有变量复制到事件参数类中。在那里更改它不会自动更改添加了 ref 的另一端的内容。这不是一个好的解决方案,但为了证明这是问题,可以在 handler(this, args) 之后添加以下内容:

message = args.Message;
functionNo = args.FunctionNo;

这会导致ref覆盖其它的值。

啊是的 - 我现在明白了。你知道有没有“好”的解决方案吗?例如,这种事情在 CancelEventArgsFormClosing 框架中完成 - 你知道怎么做吗? - Matt Wilko
是的,在那里他们使用接收方法内部的值。结果不会超出该方法的边界(如果有的话,可能会使用返回值)。 - Patrick Hofman
这实际上是一个不错的解决方案。你可以传递一个对象,而不是通过引用传递值。 - Patrick Hofman

1
似乎您认为您的“ref”参数应受到上面的代码的影响,但事实并非如此。是的,您将message作为引用传递给MessageReceivedEvent函数,但然后您将其分配给MessageReceivedEventArgs.Message,这是按值而不是按引用进行的。
结果是,当您在VB代码中修改MessageReceivedEventArgs.Message时,message变量不会受到影响(当然MessageReceivedEventArgs.Message会受到影响),尽管您已经通过引用传递了它。
您应该直接将MessageReceivedEventArgs的实例传递给您的函数(而不是在该函数内部创建它)。
private void MessageReceivedEvent(MessageReceivedEventArgs args)
{
    EventHandler<MessageReceivedEventArgs> handler = MessageReceived;
    if (handler != null)
    {
        handler(this, args);
    }
}

这似乎仍然无法工作。我已按照您的建议修改了参数,并在Connect方法中创建了一个MessageReceivedEventArgs实例并传递了它。 - Matt Wilko
如果它能够正常工作,那就好了。如果不能,请更新您的问题并提供新代码(并澄清您的目标)。 - Evk
1
传递 args 也没有帮助,因为它仍然不会复制 ref - Patrick Hofman
@PatrickHofman 我的意思是完全不同的 - 摆脱所有引用并使用一个参数实例。实际上,这是您在评论中提出的答案。但是,在更新问题后,我发现这是不可能的,因为引用在事件签名中。 - Evk
好的,我明白了。尽管如此,您的答案指向了正确的方向@Evk。 - Patrick Hofman

0
首先,您不需要使用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
            }
        }
    }
}

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