简单整理一下struct。c#
对于struct 而言呢,咱们每每会拿class做为对比,可是呢,咱们在初学阶段用class来替代struct,struct的存在感愈来愈低了。数组
那么是什么缘由使咱们常用struct呢?我感受很简单的一句话就是struct能作的class都能作,struct不能作的,class 也能作,这就是问题关键了。安全
那么先来看下他们的对比:异步
一、结构是值类型,它在栈中分配空间;而类是引用类型,它在堆中分配空间,栈中保存的只是引用。函数
二、结构类型直接存储成员数据,让其余类的数据位于堆中,位于栈中的变量保存的是指向堆中数据对象的引用。性能
结构不支持继承。this
结构不能声明默认的构造函数。code
结构类型中不能设置默认值。对象
从第二点中能够明白结构类型中,不必定存储的必定是值,还多是引用,这就打破了初学的时候误觉得结构类型只能存储值类型,还多是引用对象的引用,以下:继承
static void Main(string[] args) { var parent = new Parent(30,"张大大"); var zhangsan = new Student(1,"张三",parent); zhangsan.age = 10; } struct Student { public Student(int age, string name,Parent parent) { this.age = age; this.name = name; this.parent = parent; } public int age { get; set; } public string name { get; set; } public Parent parent { get; set; } } struct Parent { public Parent(int age, string name) { this.age = age; this.name = name; } public int age { get; set; } public string name { get; set; } }
在Student 结构中,咱们也能够去复制引用。
第三点很好理解,第四点表示咱们不能去本身声明默认构造函数。如:
public Student() { }
那么咱们何时使用struct呢?
那么要从struct 优势出发,struct 是值类型,当离开做用域的时候,那么对垃圾回收是有好处的。
一样,由于struct 是值类型,分配到堆上,若是值类型过大,这会大量占用到堆的空间,因此咱们的数据比较下。
当有大量的赋值语句的时候,那么咱们也应该避开struct,由于赋值值类型中将会拷贝所有,而不是引用。
根据上诉,实用场景为:
对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则CLR须要为每一个对象分配内存,在这种状况下,使用结构的成本较低;
从上总结出,struct能够在一些以数据为主的场景中使用,且数据量不大的状况。
在介绍readonly 以前,先介绍一下,和ref 还有out 其名的in,不是别的in哈。
static void Main(string[] args) { int readonlyArgument = 44; InArgExample(readonlyArgument); Console.WriteLine(readonlyArgument); // value is still 44 } static void InArgExample(in int number) { // Uncomment the following line to see error CS8331 //number = 19; }
这里的in 的做用是能够引用readonlyArgument,可是只读,不能修改number的值。那么这有什么用呢?我直接不设置值不就能够吗?或者说我起码设置一个readonly 这总行吧。
而咱们知道in 有不能用于异步方法,赋值消耗也不大形参,那我要这个引用有啥用?关键就在于咱们自定义的struct仍是大有好处的,struct 是咱们自定义的结构类型,这个比较大,那么这就是一个struct的突破点了,传值的时候能够传递struct。
下面介绍readonly 这个是为了安全,作为一个readonly,咱们首先就要区分的是const,const 是编译性,而readonly是运行时。这个能够百度,在此就不作过多的介绍。
经过readonly struct 还有 in,那么能够建立防护性副本。
这里值得注意的是,官网提到这样一句话:
除非使用 readonly 修饰符声明 struct或方法仅调用该结构的 readonly 成员,不然切勿将其做为 in 参数传递。 不遵照该指南可能会对性能产生负面影响,并可能致使不明确的行为
官网给出了一个这样的例子:
int readonlyArgument = 44; InArgExample(readonlyArgument); Console.WriteLine(readonlyArgument); // value is still 44 void InArgExample(in int number) { // Uncomment the following line to see error CS8331 //number = 19; }
而且说明了一段这样的话:
在首次检查时,你可能认为这些访问是安全的。 毕竟,get 访问器不该该修改对象的状态。 可是没有强制执行的语言规则。 它只是通用约定。 任何类型均可以实现修改内部状态的 get 访问器。 若是没有语言保证,编译器必须在调用任何未标记为 readonly 修饰符的成员以前建立参数的临时副本。 在堆栈上建立临时存储,将参数的值复制到临时存储中,并将每一个成员访问的值做为 this 参数复制到堆栈中。 在许多状况下,当参数类型不是 readonly struct,而且该方法调用成员未标记为 readonly 时,这些副本会下降性能, 使得按值传递比按只读引用传递速度更快。 若是将不修改结构状态的全部方法标记为 readonly,编译器就能够安全地肯定不修改结构状态,而且不须要防护性复制。
那么上面讨论了参数传递的问题,那么接下来讨论一下,参数返回的问题。
好比说返回了:
var a=getANumber(); private static int getANumber(){ var b=1; return b; }
那么其实这个a的值怎么获取的呢?是b的值赋值给a。
可是对于比较大的struct,用这种赋值的方式,就比较消耗cpu和内存了。
那么可使用ref来返回。
var a=getANumber(); private static ref int getANumber(){ var b=1; return ref b; }
这样b的引用传递给了a。
若是你但愿返回的参数不可改变,那么你能够这样:
var a=getANumber(); private static ref readonly int getANumber(){ var b=1; return ref b; }
那么这个时候有人就奇怪了,为啥ref还要 readonly 这东西呢?
举个例子:
public static ref int Find(int[,] matrix, Func<int, bool> predicate) { for (int i = 0; i < matrix.GetLength(0); i++) for (int j = 0; j < matrix.GetLength(1); j++) if (predicate(matrix[i, j])) return ref matrix[i, j]; throw new InvalidOperationException("Not found"); }
这个例子返回的是数组的一部分,若是改了这个值,那么数组里面的值不就改变了吗。
可能我这样说,加上官网这个例子不到位,可能没能表达明白。再来一个本身写的例子:
static void Main(string[] args) { var matrix =new Student[1]; matrix[0] = new Student(20,"张三",new Parent()); ref Student result =ref Find(matrix); result.age = 10; Console.WriteLine(matrix[0].age); Console.WriteLine(result.age); Console.ReadLine(); } static ref Student Find(Student[] matrix ) { return ref matrix[0]; } struct Student { public Student(int age, string name,Parent parent) { this.age = age; this.name = name; this.parent = parent; } public int age { get; set; } public string name { get; set; } public Parent parent { get; set; } }
这里打印出来两个都是10。
若是给Find 加上readonly,那么要这样写
ref readonly Student result =ref Find(matrix);
Student 天然不能再进行赋值。
上述只是我的整理和一点点我的理解,若有不对望指出。下一节,整理c# 装箱和拆箱,介绍一下生命周期。