Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 不变性。。谎言?_C#_Multithreading_Immutability - Fatal编程技术网

C# 不变性。。谎言?

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();

在多线程环境中,克隆对象(例如:列表)以提高不变性是一种理想的做法。然而,如果我们这样做,它可能是对API用户的谎言。我想说的是

考虑以下代码:

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;
    }
}