object是只读对象,引用类型。
我真心觉得初学者完全没必要搞清楚引用类型和值类型的区别,毕竟堆栈是啥都没弄明白前,不清楚声明类型和运行时类型什么区别前,怎么去理解引用类型和值类型的区别在于编译后的运行时行为。
混淆只读对象和值类型是最容易犯的错误之一。只读对象根本没有办法通过改变状态来观察到引用类型的行为,事实上用对象改变同一引用的另一对象改变来定义引用类型也是错误的,引用类型和值类型的定义是运行时行为(而这关系到了使用引用类型还是值类型的决策)。搞不懂就背书,从ValueType继承的除去Enum都是值类型,其余的都不是。
因为很多面试官喜欢考这个,所以背背就好了。
好吧我就多罗嗦几句吧,要真正理解透彻引用类型和值类型必须要到CLR的行为层面。这里只简单的说说什么是只读对象,以及怎么用代码去验证引用类型。
只读对象就是指没有办法修改其状态的对象(也就是说没有字段,或者没有公开的修改字段的方法或属性以及事件)。
例如new object(),这就是一个只读对象,"abc"也是只读对象。
用修改对象的状态来观察是引用类型还是值类型这一方法对于只读对象是无效的,因为只读对象的状态没有办法被修改。
正确的修改对象状态来观察引用类型的方式是这样的:
int[] array = new int[]{ 1, 2, 3 }; var array1 = array; Console.WriteLine( string.Join( ",", array1 ) ); array[0] = 5; Console.WriteLine( string.Join( ",", array1 ) );
int[],即数组类型,是一个典型的引用类型,其状态,即数组元素是可以修改的。所以通过修改数组元素,我们就能观察到引用类型的特性。
而值类型,绝大多数值类型都被设计为只读对象,这其实是值类型的设计原则之一。除非我们构建一个不符合设计原则的值类型,否则我们观察不到赋值的时候值类型被复制的行为。
这也是我比较反感用状态变化来说明引用类型和值类型以及非要给初学者强调引用类型和值类型区别的原因。我给初学者的建议就是没事别自己弄个struct,除非你已经完全明白你在干吗。
那么不自定义值类型,能不能观察到值类型与引用类型的行为不同呢?
可以。
{ var obj1 = new object(); var obj2 = obj1; Console.WriteLine( object.ReferenceEquals( obj1, obj2 ) ); } { var obj1 = new int(); var obj2 = obj1; Console.WriteLine( object.ReferenceEquals( obj1, obj2 ) ); }
这是比较好的判断值类型和引用类型的方法,不过原理却不是因为值类型在赋值的时候被复制了,而是因为在ReferenceEquals的时候会被装箱(装箱就是运行时行为的一个不同,只有值类型会导致装箱,引用类型不会导致装箱)。
大家不妨猜一下下面这段代码结果是什么:
{ var obj1 = new int(); Console.WriteLine( object.ReferenceEquals( obj1, obj1 ) ); }