C# 返回 ConcurrentBag 的副本

9
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace Crystal_Message
{
    class Message
    {
        private int messageID;
        private string message;
        private ConcurrentBag <Employee> messageFor;
        private Person messageFrom;
        private string calltype;

        public Message(int iden,string message, Person messageFrom, string calltype, string telephone)
        {
            this.messageID = iden;
            this.messageFor = new ConcurrentBag<Employee>();
            this.Note = message;
            this.MessageFrom = messageFrom;
            this.CallType = calltype;
        }

        public ConcurrentBag<Employee> ReturnMessageFor
        {
            get
            {

                return messageFor;
            }

        }

        public int MessageIdentification
        {
            get { return this.messageID; }

            private set
            {
                if(value == 0)
                {
                    throw new ArgumentNullException("Must have Message ID");
                }

                this.messageID = value;
            }

        }

        public string Note
        {
            get { return message; }

            private set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("Must Have a Message");

                }
                this.message = value;


            }

        }

        public Person MessageFrom
        {
            get { return messageFrom; }

            private set
            {
                this.messageFrom = value;
            }

        }

        public string CallType
        {
            get { return this.calltype; }

            private set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentNullException("Please specify call type");
                }

                this.calltype = value;

            }

        }

        public void addEmployee(Employee add)
        {
            messageFor.Add(add);
        }


        public override string ToString()
        {
            return "Message: " + this.message + " From: " + this.messageFrom + " Call Type: " + this.calltype + " For: " + this.returnMessagefor();
        }

        private string returnMessagefor() 
        {
            string generate="";

            foreach(Employee view in messageFor)
            {
                generate += view.ToString() + " ";
            }

            return generate;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            Message testEquals = obj as Message;

            if((System.Object)testEquals == null)
            {
                return false;
            }

            return (this.messageID == testEquals.messageID) && (this.message == testEquals.message) && (this.messageFor == testEquals.messageFor) && (this.messageFrom == testEquals.messageFrom) && (this.calltype == testEquals.calltype);

        }

        public bool Equals(Message p)
        {
            if ((Object)p == null)
            {
                return false;
            }

            return (this.messageID == p.messageID) && (this.message == p.message) && (this.messageFor == p.messageFor) && (this.messageFrom == p.messageFrom) && (this.calltype == p.calltype);

        }

        public override int GetHashCode()
        {


            unchecked
            {
                return this.messageID.GetHashCode() * 33 ^ this.message.GetHashCode() * 33 ^ this.messageFor.GetHashCode() * 33 ^ this.messageFrom.GetHashCode() * 33 ^ this.calltype.GetHashCode();
            }


        }

    }
}

我有一个消息类,用户可以留言给多个人。我有一个getter,但是它返回的ConcurrentBag<>是否符合规范?如果不是,那么我该如何返回ConcurrentBag<>以便可以循环遍历并显示它?

2个回答

7

ConcurrentBag<T> 是一个 IEnumerable<T>。您可以像往常一样遍历它。但是,由于这是一个线程安全的集合,使用它存在性能问题。

如果您想在循环时摆脱性能影响,请调用 ToArray 并返回新数组。

    public IEnumerable<Employee> ReturnMessageFor
    {
        get
        {

            return messageFor.ToArray();
        }

    }

@Nexusfactor,保持原样就可以了,但要注意的是,消费代码可能会向包中添加新项。这并不总是理想的。 - Gusdor
@MatthewWatson 很敏锐地发现了。然而,数组就是数组,总是按顺序和原地循环。方法的命名空间并不改变这一点。转换数组将产生与一个循环几乎相同的开销,但对于许多循环/用途来说,数组更便宜。 - Gusdor
实际上,ConcurrentBag<> 中的实现不仅仅是循环。它会对袋子进行快照,因此在制作副本时会被锁定。它调用内部方法 FreezeBag()UnfreezeBag() 来完成这个过程。 - Matthew Watson
非常相关 - 这意味着这是一种更好的方法!这意味着在复制时容器的内容不能改变,在某些多线程场景中这可能非常重要。由于这是一个特定的并发容器,任何与并发相关的事情都非常重要。 - Matthew Watson
1
@Nexusfactor请仔细阅读文档,如果您仍然卡住了,请提出另一个问题。 - Gusdor
显示剩余11条评论

2

我不清楚你想要达到什么目的。

你是想将Bag用于所有操作吗?因为这就是你所做的...

如果你想要将某些可以迭代的东西外部化,那么你应该返回Bag作为IEnumerable,或者返回从Bag复制出来的数组或列表。

无论哪种方式都是安全的迭代方式。可能在性能方面不是最好的,但那是另一个问题。

    // Option 1
    public IEnumerable<Employee> ReturnMessageFor
    {
        get
        {
            return messageFor;
        }
    }

    // Option 2
    public Employee[] ReturnMessageFor
    {
        get
        {
            return messageFor.ToArray();
        }
    }

注意:

  • 您可能想要将messageFor设置为只读(在您发布的代码中它是只读的)。
  • 请记住,ConcurrentBag允许您以线程安全的方式安全地迭代集合的快照,但它不会锁定集合中的项。

我想要的只是获取集合并对其进行迭代。但是,我不希望用户在其他地方修改集合。我喜欢你的第二种方法。返回一个数组,而且你不能以任何方式修改messagefor,对吗? - user4981830
没错。ToArray 返回集合的 _浅拷贝_,因此 removeFor 不能被更改。 - Yosef O

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