我刚刚学习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&lt; ...&gt;(...)或AsReadOnly()进行读取,但在这种情况下如何将新学生添加到列表中?
避免/解决此问题的最佳做法是什么?
答案 0 :(得分:4)
您不应该公开列表&lt;&gt;你班上的财产 - 它几乎从来都不是一个好主意,因为它给了班级用户过多的数据控制权,在这种情况下肯定不是一个好的选择。相反,您应该只公开所需的属性,并执行自己的验证:
// 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);
}
答案 1 :(得分:1)
我建议使用Dictionary
,因为它非常适合根据Key
来防止重复。它也比检查重复项时只使用List
要快得多。
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
公开为IEnumerable
,因此很明显它无法直接修改,因为它没有Add
或Remove
。 _Students
,Dictionary
用作此属性的支持者/来源。
我还展示了AddStudent
方法,该方法将处理添加学生,并检查重复项Numbers
。
进一步澄清:
_Students
为protected
,因此从SchoolClass
派生的任何类都可以访问它。在这种情况下似乎很可能。
我没有为set { }
定义Students
,因为它是一个不应该直接修改的集合。不创建提供set
使其成为唯一的修改方法是修改_Students
(其支持者)。
答案 2 :(得分:0)
您可以将getter标记为readonly并编写以下方法以添加新学生
public void AddStudent(Student s)
{
if(!this.studentList.Contains(s))
this.studentList.Add(s);
else
throw new Exception("Duplicate entry");
}
Contains()仅在学生班级中有Equals Implemented时才有用。相反,您可以使用this.studentList.Any(s => s.Number == s.Number)
@ pauls的评论,HashSet也是一个不错的选择...
答案 3 :(得分:0)
您可以使用System.Collections.ObjectModel.KeyedCollection:
public class SchoolClass : KeyedCollection<uint, Student>
{
protected override uint GetKeyForItem(Student newStudent)
{
return newStudent.Number;
}
}
当具有给定号码的学生已经存在时,这将抛出ArgumentException
答案 4 :(得分:0)
由于您使用getter中的列表instance.Property.Operation
,因此您无需通过setter直接操作列表。只读属性可能不会为您提供所需的行为,因为只读属性只是没有setter的属性。
您实际上可以使用.AsReadOnly()
方法,结合SchoolClass类的新方法,例如AddStudent(Student student)
和AddStudents(List<Students> students)
。 AddStudents
方法可以在循环中使用AddStudent
方法,而这又可以在您的属性上使用私有的setter。验证可能会在私有设置器中完成。
请参阅http://msdn.microsoft.com/en-us/library/75e8y5dd.aspx和http://msdn.microsoft.com/en-us/library/e78dcd75.aspx
答案 5 :(得分:0)
我认为您需要将List.Contains方法与自定义IEqualityComparer一起使用。因此,您可以与其他财产以及数字进行比较。因此,如果需要进行任何更改,您将无需更改代码即可提供。
请参阅this link