为什么列表会强制所有新项目自我复制?

Why does the list force all new items to duplicate themselves?

我已经大大减少了项目中的代码,因此它可以复制和粘贴,但是如果您想在控制台项目中进行调试,则需要 nuget 包:Install-Package MsgPack.Cli.

好的,我在下面评论了问题所在的行,我想知道的是为什么列表在 _outgoingMessageQueue 队列中强制重复。这是某种捕获变量难题吗?请尽可能详细

using MsgPack.Serialization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;

namespace QueueTest
{
    public class Message
    {
        public string Data { get; set; }
    }

    public class InternalFactoryMsg<T>
    {
        public T Data { get; set; }
        public string Group { get; set; }
        public List<byte[]> ReturnIds { get; set; }
    }

    public class FactoryHelpers
    {
        public static List<byte[]> GetReturnIdentities(List<byte[]> messageBytes, byte[] identity)
        {
            var response = new List<byte[]>();

            foreach (byte[] part in messageBytes)
            {
                if (part != null && part.Length > 0)
                    response.Add(part);
                else
                    break;
            }

            // may not need this with good routing, but can avoid errors
            if (messageBytes.Count > 0 && messageBytes[0] == identity)
            {
                messageBytes.RemoveAt(0);
                Console.WriteLine("[GetReturnIdentities]: Removed identity from start, check your routing!");
            }

            // no return identities, send empty list as these bytes will be the 
            // app message and command identity couple
            if (response.Count == messageBytes.Count)
                return new List<byte[]>();

            return response;
        }

        public static byte[] SerializeData<T>(T appMsg)
        {
            var serializer = MessagePackSerializer.Get<T>();

            using (var byteStream = new MemoryStream())
            {
                serializer.Pack(byteStream, appMsg);
                return byteStream.ToArray();
            }
        }

        public static T DeserializeData<T>(byte[] bytes)
        {
            try
            {
                var serializer = MessagePackSerializer.Get<T>();
                using (var byteStream = new MemoryStream(bytes))
                {
                    return serializer.Unpack(byteStream);
                }
            }
            catch (Exception ex)
            {
                return default(T);
            }
        }
    }

    public class Factory: FactoryHelpers
    {
        protected ConcurrentQueue<KeyValuePair<string, List<byte[]>>> _outgoingMessageQueue { get; set; }
        public ConcurrentQueue<KeyValuePair<string, List<byte[]>>> IncomingMessageQueue { get; set; }

        public Factory()
        {
            _outgoingMessageQueue = new ConcurrentQueue<KeyValuePair<string, List<byte[]>>>();
            IncomingMessageQueue = new ConcurrentQueue<KeyValuePair<string, List<byte[]>>>();

            // add fake incoming message
            var byteMsg = new List<byte[]>()
            {
                Encoding.Unicode.GetBytes("socket1"),
                Encoding.Unicode.GetBytes(""),
                Encoding.Unicode.GetBytes("data")
            };
            var msg = new KeyValuePair<string, List<byte[]>>("socket1", byteMsg);
            IncomingMessageQueue.Enqueue(msg);
        }

        public void AddMessage<T>(InternalFactoryMsg<T> msg)
        {
            var msgBytes = msg.ReturnIds ?? new List<byte[]>();
            msgBytes.Add(new byte[0]);
            msgBytes.Add(Factory.SerializeData<T>(msg.Data));

            _outgoingMessageQueue.Enqueue(new KeyValuePair<string, List<byte[]>>("socket2", msgBytes));
        }

        public List<KeyValuePair<string, List<byte[]>>> GetQueue()
        {
            return _outgoingMessageQueue.ToList();
        }

        public static T GetDataFromBytes<T>(List<byte[]> msgBytes)
        {
            // ignoring null checks etc
            return DeserializeData<T>(msgBytes.Last());
        }
    }

    public static class MessageLayer
    {
        public static Factory Factory = new Factory();

        public static void Init()
        {
            Task.Factory.StartNew(u =>
            {
                while(true)
                {
                    KeyValuePair<string, List<byte[]>> msg;
                    if(Factory.IncomingMessageQueue.TryDequeue(out msg))
                    {
                        var data = msg.Value.Last();
                        var returnIds = Factory.GetReturnIdentities(msg.Value, Encoding.Unicode.GetBytes(msg.Key));
                        IncomingCommands.HandleDataCommand(data, "test grp", returnIds);
                    }
                    // nice and slow for simulation
                    Thread.Sleep(400);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public static void SendMessage(Message msg, string group, List<byte[]> returnIds)
        {
            var intMsg = new InternalFactoryMsg<Message>();
            intMsg.Data = msg;
            intMsg.Group = group;
            intMsg.ReturnIds = returnIds;
            Factory.AddMessage<Message>(intMsg);
        }
    }

    public static class DataAccessor
    {
        public static List<Message> GetData(byte[] data)
        {
            return new List<Message>() 
            {
                new Message() { Data = "magic" },
                new Message() { Data = "data!" }
            };
        }
    }

    public static class IncomingCommands
    {
        public static void HandleDataCommand(byte[] data, string group, List<byte[]> returnIds)
        {
            List<Message> result;
            // does big switch, gets data response
            result = DataAccessor.GetData(data);

            foreach (Message msg in result)
            {
                var local = msg;
                var fix = new List<byte[]>(returnIds);
                // THIS IS THE ISSUE
                // comment out the following line and uncomment the one below to fix it
                // but... why??? :O !!!
                MessageLayer.SendMessage(local, group, returnIds);
                //MessageLayer.SendMessage(local, group, fix);
            }

            // check the queue
            Console.WriteLine("---------------------------");
            Console.WriteLine("::Checking queue contents::");
            var msgs = MessageLayer.Factory.GetQueue();

            foreach(var m in msgs)
            {
                var check = Factory.GetDataFromBytes<Message>(m.Value);
                Console.WriteLine("data -> " + check.Data);
            }
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            MessageLayer.Init();

            while(true)
            {
                Thread.Sleep(400);
            }
        }
    }
}

如果您无法解决,请投票以引起注意。谢谢

原因是

var msgBytes = msg.ReturnIds ?? new List<byte[]>();

导致变量捕获,这意味着后续使用会强制重复引用同一对象