C# 不变性。。谎言?
在多线程环境中,克隆对象(例如:列表)以提高不变性是一种理想的做法。然而,如果我们这样做,它可能是对API用户的谎言。我想说的是 考虑以下代码:C# 不变性。。谎言?,c#,multithreading,immutability,C#,Multithreading,Immutability,在多线程环境中,克隆对象(例如:列表)以提高不变性是一种理想的做法。然而,如果我们这样做,它可能是对API用户的谎言。我想说的是 考虑以下代码: public class Teacher { public List<Student> Students = new List<Student>(); public Student GetStudent(int index) { return Students[index].Clone();
public class Teacher {
public List<Student> Students = new List<Student>();
public Student GetStudent(int index) {
return Students[index].Clone();
}
}
public class Student {
public DateTime LastAttended { get; set; }
}
如果没有适当的文档,用户不可能知道他得到的学生对象实际上是一个克隆对象,在这种情况下,对该对象所做的所有更改都不会反映原始对象
如何改进上面的代码,让用户直观地知道GetStudent只用于阅读而不用于修改?有没有办法强制/限制修改从GetStudent方法返回的Student对象?您的Student对象根本不是不可变的。如果想要不可变,请创建不可变对象:
public sealed class Student {
private readonly DateTime _lastAttended;
public DateTime LastAttended { get { return _lastAttended; } }
public Student(DateTime lastAttended)
{
_lastAttended = lastAttended;
}
}
如果您不希望有人设置属性的值,那么不要公开setter,只公开getter
这当然需要围绕这一点构建应用程序。如果您确实需要更新LastAttended time,您可以这样做,例如通过一个存储库更新数据库并返回一个新的Student对象。此外,许多ORM不能自动处理不可变的对象,需要一些翻译代码
请注意,当人们将对象缓存在内存中,然后将其传递给其他人时,您的问题非常常见,例如查看操纵对象的模型,在不知情的情况下修改缓存中的主对象。这就是为什么通常建议将克隆用于缓存。克隆可以防止您对“您的”对象进行代码修改—每次有人请求时,他们都会得到您的主对象的新实例。克隆并不能防止调用方弄乱自己的版本
请注意,如果字段的类型是可变类型,则将字段声明为只读并没有多大作用-我仍然可以这样做,例如Student.Course.Name=“Test”代码>即使课程是只读的
-我不能更改Student对象中的引用,但我可以访问任何属性设置器
真正的不变性在C#中有点麻烦,因为它需要大量的类型和工厂方法。在某种程度上,离开一个正常的可变Get/Set并相信调用方知道该做什么是可以的,因为他们只会把自己搞得一团糟,而不是你。也就是说,任何实际操作数据库中数据的操作都需要适当的安全/业务规则检查。这不是不变性的含义。而且,你是对的;这是一个坏主意,除非有详细的文档。GetStudent
和GetStudentClone
。。。这是自我记录。我想你可能在寻找struct
s?谁说克隆是提供不变性的理想做法?如果您想要不可变,首先要创建不可变对象,或者使用阻止某些修改的属性设置器(但反射仍然有效)。顺便说一句,如果您真的希望有人调用该方法,最好将列表设置为私有。否则人们可能只做教师.学生[0]
之类的事情。
public sealed class Student {
private readonly DateTime _lastAttended;
public DateTime LastAttended { get { return _lastAttended; } }
public Student(DateTime lastAttended)
{
_lastAttended = lastAttended;
}
}