3

我刚刚在学习 OOP,但我在练习时遇到了问题。我有一个有学生的 SchoolClass。一个 SchoolClass 中的学生不允许有相同的编号。我有以下内容:

class SchoolClass 
{
    private List<Student> students;

    public List<Student> Students
    {
        get { return this.students; }
        set
        {
            if (value == null) throw new ArgumentNullException("Students list can not be null!");
            if (value.Select(n => n.Number).Distinct().Count() != value.Count())
                throw new ArgumentException("There are students in the list with the same class number!");
            this.students = value;
        }
    }
public SchoolClass( List<Student> students)
    {
        this.Students = students;
    }
}
class Student 
{
    private uint number;

    public uint Number
    {
        get { return this.number; }
        set
        {
            if (value == 0) throw new ArgumentException("Student's number can not be null!");
            else this.number = value;
        }
    }

    public Student(string name, uint number)
    {
        this.Number = number;
    }
}

当我在 Main() 类中用相同的编号初始化两个学生时,我有一个 ArgumentException("列表中有学生具有相同的类号!"); 正如预期的那样。但是当我有一个 SchoolClass (sc) 的实例并调用它的学生列表并使用 Add() (sc.students.Add(new Student(..)) 我可以插入一个具有重复数字的学生。同样是List.InsertAt() 方法。我读到 getter 可以使用 ReadOnlyCollection<...>(...) 或 AsReadOnly() 以只读方式完成,但在这种情况下如何将新学生添加到列表中?

避免/解决此问题的最佳做法是什么?

4

6 回答 6

4

你不应该在你的类上公开 List<> 属性——这几乎不是一个好主意,因为它让类的用户对你的数据有太多的控制权,在这种情况下它肯定不是一个好的选择。相反,您应该只公开您需要的属性,并执行您自己的验证:

// store the students as a List<>...
private List<Student> students;

// ...but expose them as IEnumerable<>.
public IEnumerable<Student> Students
{
    get { return this.students; }
}

// IEnumerable<> does not allow adding, removing, clearing, etc - 
// you know you're the only one altering the data

// and you can choose to give them restricted access with your own
// validation logic
public void AddStudent(Student student) 
{
    if (this.students.Any(s => s.Number == student.Number))
        throw new ArgumentException("Duplicate student number!");
    this.students.Add(student);
}
于 2013-10-09T12:27:27.240 回答
1

我建议使用 a Dictionary,因为它非常适合根据 a 来避免重复KeyList它也比只使用一段时间检查重复项要快得多。

class SchoolClass
{
    protected Dictionary<uint, Student> _Students = new Dictionary<uint, Student>();
    public IEnumerable<Student> Students
    {
        get { return _Students.Values; }
    }

    public SchoolClass(List<Student> students)
    {
        if (students == null) throw new ArgumentNullException("Students list can not be null!");

        foreach (var student in students)
            AddStudent(student);
    }

    public void AddStudent(Student student)
    {
        if (_Students.ContainsKey(student.Number))
            throw new ArgumentException("There are students in the list with the same class number!");

        _Students.Add(student.Number, student);
    }
}

在示例中,我公开Students为 an IEnumerable,因此很明显它不能直接修改,因为它没有Addor Remove_Students, aDictionary用作此属性的支持者/来源。

我还公开了AddStudent一种处理添加学生和检查重复项的方法Numbers


进一步澄清

_Students这样protected任何派生的类都SchoolClass可以访问它。在这种情况下似乎很有可能。

我没有定义一个set { }forStudents因为它是一个不应该直接修改的集合。不创建提供 aset使得它因此修改它的唯一方法是通过修改_Students(它的支持者)。

于 2013-10-09T12:56:47.967 回答
0

由于您将 getter 中的列表用作instance.Property.Operation,因此您无需通过 setter 即可直接操作列表。只读属性可能不会给你想要的行为,因为只读属性只是一个没有设置器的属性。

实际上,您可以使用该.AsReadOnly()方法,并结合 SchoolClass 类上的新方法,例如AddStudent(Student student)and AddStudents(List<Students> students)。该AddStudents方法可以AddStudent在循环中使用该方法,进而可以在您的属性上使用私有设置器。验证可能会在私人二传手中完成。

请参阅http://msdn.microsoft.com/en-us/library/75e8y5dd.aspxhttp://msdn.microsoft.com/en-us/library/e78dcd75.aspx

于 2013-10-09T12:39:34.627 回答
0

您可以将 getter 标记为只读并编写以下方法来添加新学生,

public void AddStudent(Student s)
{
    if(!this.studentList.Contains(s))
    this.studentList.Add(s);
    else
     throw new Exception("Duplicate entry");
}

Contains() 仅在您在学生班级中实施了 Equals 时才有帮助。相反,您可以使用this.studentList.Any(s => s.Number == s.Number) @pauls 的评论,HashSet 也是一个不错的选择...

于 2013-10-09T12:26:53.027 回答
0

您可以使用 System.Collections.ObjectModel.KeyedCollection:

public class SchoolClass : KeyedCollection<uint, Student>
    {
        protected override uint GetKeyForItem(Student newStudent)
        {
            return newStudent.Number;
        }
    }

当具有给定数字的学生已经存在时,这将引发 ArgumentException

于 2013-10-09T12:35:29.213 回答
0

我认为您需要将 List.Contains 方法与自定义 IEqualityComparer 一起使用。因此,您可以与其他属性以及数字进行比较。因此,如果需要任何更改,您将无需更改太多代码即可进行配置。

请参考这个链接

于 2013-10-09T12:43:38.827 回答