不可变对象
不可变(immutable): 即对象一旦被创建初始化后,它们的值就不能被改变,之后的每次改变都会产生一个新对象。
1 2 |
var str="mushroomsir"; str.Substring(0, 6) |
c#中的string是不可变的,Substring(0, 6)返回的是一个新字符串值,而原字符串在共享域中是不变的。另外一个StringBuilder是可变的,这也是推荐使用StringBuilder的原因。
1 |
var age=18; |
当存储值18的内存分配给age变量时,它的内存值也是不可以被修改的。
1 |
age=2; |
此时会在栈中开辟新值2赋值给age变量,而不能改变18这个内存里的值,int在c#中也是不可变的。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Contact { public string Name { get; set; } public string Address { get; set; } public Contact(string contactName, string contactAddress) { Name = contactName; Address = contactAddress; } } var mutable = new Contact("二毛", "清华"); mutable.Name = "大毛"; mutable.Address = "北大"; |
我们实例化MutableContact赋值给mutable,随后我们可以修改MutableContact对象内部字段值,它已经不是初始后的值,可称为可变(mutable)对象。
可变对象在多线程并发中共享,是存在一些问题的。多线程下A线程赋值到 Name = “大毛” 这一步,其他的线程有可能读取到的数据就是:
1 2 |
mutable.Name == "大毛"; mutable.Address == "清华"; |
很明显这样数据完整性就不能保障,也有称数据撕裂。我们把可变对象更改为不可变对象如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Contact2 { public string Name { get; private set; } public string Address { get; private set; } private Contact2(string contactName, string contactAddress) { Name = contactName; Address = contactAddress; } public static Contact2 CreateContact(string name, string address) { return new Contact2(name, address); } } |
使用时只能通过Contact2的构造函数来初始化Name和Address字段。Contact2此时即为不可变对象,因为对象本身是个不可变整体。通过使用不可变对象可以不用担心数据完整性,也能保证数据安全性,不会被其他线程修改。
自定义不可变集合
我们去枚举可变集合时,出于线程安全的考虑我们往往需要进行加锁处理,防止该集合在其他线程被修改,而使用不可变集合则能避免这个问题。我们平常使用的数据结构都是采用可变模式来实现的,那怎么实现一个不可变数据结构呢!以栈来示例,具体代码如下: