起因之前一直写C#,因为有GC,所以不用关心对象的复制问题,默认的浅复制就够了,也就没去深究struct、class、值、引用间的区别,前段时间写了点C++,C++需要手动管理内存,如果类包含有指针或引用成员时就要遵循rule of three,实现复制、赋值复制和析构以正确管理资源,这种生命周期管理函数写多了感觉挺枯燥的,而且我发现许多domain object没有必要实现复制,因为大多数domain object并不适合用值语义来表达,首先它们本质不是一种“值”,并且我希望传递时传递同一个对象,而不是一个副本。 C#中同时保留了值类型和引用类型,而我几乎从未写过自定义的值类型。于是我就开始思考值对象和引用对象到底有什么区别,随即写下此文分享本人的见解。 值对象(value object)什么是值对象
你现在拿出一张纸币,它就是典型的值对象,虽然每张纸币都有一个唯一的编号,但实际使用中这个编号是没有意义的,我们关心的是它们的面值。 这里纸币的编号就对应值对象的标识(比如内存地址),纸币的面值就对应值对象表示的值。 其它值对象的例子:IP地址、RGB颜色、GUID、地理坐标、日期时间。 我的理解:值对象是用来表达一个信息的对象,它的状态是静态的,且比较“透明”;我们使用值对象时,关心的是它所表达的信息,而不是这个对象本身 值对象与不可变性(immutability)值对象应该被设计成不可变的(immutable),因为
这里的问题在于我们关心的其实只是一个“日期值”而不是一个java.util.Date对象,所以在传递时应该是传递表示的日期值,而不是直接传递java.util.Date对象。注释中的FIXME标注是一种解决办法。 值对象必须要实现为不可变吗?但是如果把一个类实现为不可变的话,意味着修改一个成员就要创建一个新的对象,如果成员非常多的话,代码写起来会比较繁琐。
值对象的实现
与值对象相关的概念struct与class不管是C++和C#中的struct关键字,还是ruby中的Struct::new都是趋向用于定义简单的复合类型,所以struct适合用来定义没有复杂行为和状态的值对象。而class更趋向用于定义具有丰富逻辑、复杂状态的对象类型,struct、class和值对象、引用对象并不是一一对应的关系,但一般而言,值对象都不会太复杂。 字符串抛开具体的实现,字符串是一个静态的字符序列,它的状态决定了它的相等性,是一种值。
“引用类型”是具体语言/平台实现中的概念,“值对象”,“引用对象”是语言无关的,引用类型的对象也可能是值对象,只是引用类型的对象不具有原生的值语义。 将字符串实现为”引用类型“更多是出于性能考虑。
C#示例:
问题2,为什么C++中的std::basic_string和Ruby中的String都是可变的? 很简单,这样用起来更方便。对于C++,可以用const实现不可变性,而Ruby是通过Symbol来表示唯一的、不可变的字符串。 C#中的值类型C#语言中的类型分为两类“值类型”和“引用类型”,strut和enum属值类型,class属引用类型。C#编译器处理struct时让该类型继承了System.ValueType这个抽象类,而enum则继承自System.Enum,System.Enum还是继承自System.ValueObject。 值类型 和 引用类型的区别只有一个:一个是值语义(传值,复制),一个是引用语义(传引用),至于什么“一个分配于栈,一个分配于堆”,这是具体实现的问题,而且值类型不一定分配于栈,比如作为引用类型的成员(被捕捉到闭包中同属该情况)。 引用对象(reference object)
为什么“相等性取决于标识”?因为在使用引用对象时,我们关心的是这个对象本身,在传递过程中,需要传递同一个对象,所以需要传递对象的“引用(即标识、一般是内存地址)”,这也就是“引用语义(reference semantics)”。 我们实际写程序中,使用对象的目的在于映射问题域中的事物,基本关心的是对象本身,所以对象大多都属于引用对象。 实现
参考
|
|
来自: weijianian > 《asp.net》