【问题标题】:In C#, how can I avoid modifications when passing an array to event handlers?在 C# 中,如何在将数组传递给事件处理程序时避免修改?
【发布时间】:2023-04-07 18:15:01
【问题描述】:

我有一个 C# 类,它封装了一个 UDP 套接字,以便更轻松地接收和处理网络数据。

每当接收到数据时,都会触发一个事件,并将数据作为参数传递:

public event Action<byte[]> DataReceived = delegate(byte[] data) { };

我收到数据并像这样触发事件:

while (socket.IsBound)
{
    var buffer = new byte[MaximumDataLength];

    socket.Receive(buffer);

    DataReceived(buffer);
}

我的理解是同一个数组实例被传递给每个事件处理程序。

  1. 如果其中一个处理程序修改后,这可能会导致问题 在其他处理程序开始处理它之前的数组?

  2. 如果是这样,有什么好的方法可以解决这个问题?

【问题讨论】:

  • 传递数组的副本。
  • 如何将数组的副本传递给每个事件处理程序?效率高吗?
  • 我不知道你的情况,但这是 arr 的副本。 arr.ToArray() 。在您的代码正常工作之前,效率不是您的主要问题。
  • 是的,这会给我一个数组的副本,但是我如何给每个处理程序自己的副本?
  • 我已经添加了用于触发事件的代码 - 但它与问题并不真正相关。

标签: c# arrays events event-handling


【解决方案1】:

如果您可以选择将其设为IEnumerable&lt;byte&gt;,如下所示:

Action<IEnumerable<byte>> DataReceived = (d) => { ... }

然后你可以用任何可枚举的东西来调用它,但委托以不可修改的形式获取它。

(如果这对你不起作用,总是有ReadOnlyCollection&lt;T&gt;


澄清:

arrays implement IEnumerable&lt;&gt;(从.net 2.0 开始),您可以将byte[] 直接传递给处理程序:

byte[] buffer = SomeReadFunction();
DataReceived(buffer);

如果您想让阅读代码的人明确说明,您可以随时转换:

DataReceived((IEnumerable<byte>)buffer);

【讨论】:

  • 所以我的事件触发代码看起来像 DataReceived(buffer.ToList().AsReadOnly()); ?
  • 啊,是的,这是有道理的。这里的关键是IEnumerable 没有暴露修饰符。
  • 您还可以获得无需更改订阅者即可更改内部存储的好处。想要将其更改为Queue&lt;&gt;List&lt;&gt;?没问题。您将缓冲区变成了实现细节,而不是合同义务。
  • @Biscuits - 在很多情况下,您希望对象是不可变的。还有我在上面的评论中提到的好处。还有Arrays considered somewhat harmful
  • @theB 但是我可以通过简单的显式转换来破坏您的“安全性”。如果你真的需要它是不可变的,它可以正确地完成。
【解决方案2】:

方法一

使用Array.AsReadOnly&lt;T&gt; 扩展方法以高效的方式创建ReadOnlyCollection&lt;T&gt;

https://msdn.microsoft.com/en-us/library/53kysx7b(v=vs.110).aspx

方法二

您可以为ArraySegment&lt;T&gt; 实现一个包装器,使其成为只读,如this answer中所建议的那样。

https://stackoverflow.com/a/5757109/2707705

在列表或其他集合上使用 ArraySegment 有很多好处。

what is the use of ArraySegment<T> class?

备注

一个典型的想法可能是制作字节数组的副本,但这可能对您不起作用,因为每个事件处理程序仍将访问同一个副本。

尝试通过坚持使用 .NET 开发人员熟悉的模式(正如您已经拥有的那样)来完全避免这个问题 - 传递数组原语并相信其他代码不会做奇怪或恶意的事情通常是可以接受的 - .NET Framework 充满了这个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-28
    • 2011-05-02
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 2012-08-30
    相关资源
    最近更新 更多