C#的引用类型 和 值类型
目录
值类型和引用类型对比
类型分类 | 类型基类 | 特点 | C#中的类型代表 | unity中的类型 |
引用类型 | System.Object |
1.可以派生 2.每个值对象开头都需要包含一个数据块标识对象的实际类型及一些其他信息。 3.他的值 是一个引用,该引用指向一个对象。 4.引用类型的实例总是存储在堆中,即引用类型的值所引用的对象。 |
class,delegate 数组类型比如int[] 接口interface 字符串 容器 |
组件、Asset |
值类型 |
System.ValueType(也是派生自System.Object) |
1.值类型 不能派生 2.其值不需要额外的信息来描述值实际是什么类型 3.他的值 就是表达式的值 4.局部变量内存分配 栈上 |
int,char,float,struct enum枚举 (GetType().isValueType) |
向量、矩阵 |
字符串String类型:
字符串String是不可变(immutable),对字符串进行操作的方法实际上返回的是新的字符串对象,如下图所示,每次操作都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,创建新的String对象相关的系统开销可能会非常昂贵。如果只是修改字符串而不创建新的对象,则可以使用 StringBuilder 类(.vs StringBuilderCache类)
一些误区认识
误区一,结构是轻量级的类:
反例:DateTime类型
值类型的优点:不需要垃圾回收,(除非被装箱)不会因类型标识而产生开销,不需要解引用。
引用类型的优点:在传递参数 赋值 返回值等操作 只需要赋值4(32位CLR)或8字节(64位CLR)
误区二,"引用类型的实例总是在堆上创建,值..栈..."
后半句话是错误的。也可能在堆上
误区三,"对象在C#中默认是通过引用传递的"
是通过值传递的,只不过C# 中引用类型的值就是对对象的引用,所以默认传递的还是这个值,传递的值就是引用。
但是引用传递 pass by reference的定义相当复杂,要涉及左值和类似的计算机科学术语。如果想实现引用语义,则需要显式使用 ref 或者 out 关键字(应该尽量使用 out,除非必须用 ref)。
举个例子:
class Test
{
public int a = 7;
}
private Test test = new Test();
void AppendString(Test str)
{
//1)其实进行了解引用,改变的是所引用的对象
str.a += 9;
// 2)值的赋值
str = null;
}
void TestFunc()
{
AppendString(test);
Debug.Log(test.a);
}
如果只执行1)输出 16 改变了该引用所指的对象的值,由于str和test所值同个对象,故改变了test.a;
如果只执行2)输出 7 只是改变了传进去str的值,并没有影响test的值
从2)可以看出 并不是把引用传递进入了 传递的是值。
如果要传引用 需要显式 ref out
装箱和拆箱:值类型和object的转换
int i = 5;
// 装箱,object需要一个引用, 但是i是5是整数值,所以在运行时会在堆上创建一个包含数值5的对象。
// 然后把该对象赋值给o;悄悄进行,比如把值 作为接口类型的参数 来传递
object o = i;
// 拆箱,明确的显式一个类型转换
int j = (int)o;
调用值类型的ToString Equals GetHashCode总是伴随着装箱操作,因为这些是class Object基类的虚函数
如果是值类型就不需要用GetType()了 直接用typeof即可 因为类型信息已经知道,还避免了装箱操作。
因为装箱会创建对象,如果大量进行装箱操作,会加重垃圾回收器的负担。(装箱影响堆内存分配,和垃圾回收)
参考:
https://www.cnblogs.com/bakuhert/articles/5878086.html
深入理解C#
上一篇: 面试算法题(1)--链表反转
下一篇: 剑指offer 从尾到头打印链表