似乎无法通过引用传递 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
}
}
}
}
我有一个引发事件的 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
}
}
}
}