List<T>清空问题

6
请看一下代码。浏览一下不应该花费太长时间。
class Teacher
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _message;
        public string Message
        {
            get { return _message; }
            set { _message = value; }
        }

        public Teacher(int id, string msg)
        {
            _id = id;
            _message = msg;
        }

        private List<Course> _items;
        public List<Course> GetCourses()
        {
            return _items;
        }

        public Teacher()
        {
            if (_items == null)
            {
                _items = new List<Course>();
            }

            _items.Add(new Course(1, "cpp"));
            _items.Add(new Course(1, "java"));
            _items.Add(new Course(1, "cs"));
        }

        public void Show()
        {
            Console.WriteLine(this._id);
            Console.WriteLine(this._message);
        }

        public void ShowList()
        {
            foreach(Course c in _items)
            {
                c.Show();
            }
        }
    }

    class Course
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _message;
        public string Message
        {
            get { return _message; }
            set { _message = value; }
        }

        public Course(int id, string msg)
        {
            _id = id;
            _message = msg;
        }

        private List<Teacher> _items;
        public List<Teacher> GetTeachers()
        {
            return _items;
        }

        public Course()
        {
            if(_items == null)
            {
                _items = new List<Teacher>();
            }

            _items.Add(new Teacher(1, "ttt"));
            _items.Add(new Teacher(1, "ppp"));
            _items.Add(new Teacher(1, "mmm"));
        }

        public void Show()
        {
            Console.WriteLine(this._id);
            Console.WriteLine(this._message);
        }

        public void ShowList()
        {
            foreach (Teacher t in _items)
            {
                t.Show();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Teacher t = new Teacher();
            t.ID = 1;
            t.Message = "Damn";

            t.Show();
            t.ShowList();

            t.GetCourses().Clear();

            t.Show();
            t.ShowList();

            Console.ReadLine();
        }
    }

GetCourses()返回了_items的一个引用,所以调用t.GetCourses().Clear();会清除Teacher实例中的底层Course列表。

我希望防止这种行为。也就是说,GetCourses()将返回一个列表,但它不可修改。

如何实现?

2个回答

15
你可以创建列表的副本,或将其包装在ReadOnlyCollection中。
private List<Course> _items;
public IList<Course> GetCourses()
{
    return new List<Course>(_items);
}
或。
private List<Course> _items;
public IList<Course> GetCourses()
{
    return new ReadOnlyCollection<Course>(_items);
}
第一种选项创建了一个独立的列表 - 调用者可以修改它,添加或删除项目,但这些更改不会在teacher对象的列表中显示。第二个选项只是现有列表的包装器 - 因此对集合的任何更改都将通过包装器可见。调用者无法对集合进行任何更改。
请注意,在两种情况下,如果列表引用的Course对象的数据发生更改,这些更改都将被以任何方式显示 - 如果要阻止这种情况发生,您需要克隆每个课程。

现在C#是这样做的吗?我已经不熟悉C#了,但你会像那样创建一个新的List,还是会以某种方式克隆列表?我会怀疑List包含一种方法来深度复制列表。 - Thomas Owens
2
@Thomas:我认为List<T>中没有什么可以创建深度克隆的东西。根据我的经验,克隆通常是不鼓励的。 - Jon Skeet

5
如何返回一个 IEnumerable<Course>

略微偏题:如果你真的想要返回一个可添加、清除等操作的列表,你应该返回一个 Collection<T> 而不是一个 List<T>,或者甚至是其中的一个接口,例如 ICollection<T>。通常情况下,建议尽可能返回最严格的类型,因为后期放宽限制比缩小范围更容易。

调用方可以将其转换回List<Course>并仍然修改基础列表。 - heijp06

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