马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
区别
C# 中的类(Class)和结构体(Struct)是两种不同的数据类型,它们在很多方面有相似之处,但也存在一些关键的区别:
- 继承:
- 类可以继承其他类或实现接口。
- 结构体不能继承其他结构体或类,也不能被继承,但可以实现接口。
- 默认访问修饰符:
- 类的成员默认黑白公开的(private),必要显式指定访问级别。
- 结构体的所有成员默认是公开的(public),并且不能是私有的。
- 实例化:
- 类的实例是在堆上分配的,可以通过 new 关键字来创建。
- 结构体的实例可以在堆上或栈上分配,但通常在栈上分配,且不必要 new 关键字。
- 可变性:
- 类的实例可以是可变的,即其状态可以在创建后被修改。
- 结构体是值类型,其状态在创建后通常被认为是不可变的,只管可以通过引用传递来修改其成员。
- 垃圾采取:
- 类的实例由垃圾采取器管理,因此涉及到垃圾采取的开销。
- 结构体由于通常在栈上分配,通常不会有垃圾采取的开销,但当作为对象的一部分或分配在堆上时,其生命周期结束时也必要垃圾采取。
- 利用场景:
- 类通常用于表示具有复杂行为的对象,必要继承或多态性的场景。
- 结构体通常用于表示简单的数据结构,如简单的数据容器或轻量级的数据类型。
- 方法和属性:
- 类和结构体都可以有方法、属性、索引器、事件和构造函数。
- 静态成员:
- 类和结构体都可以有静态成员,但静态构造函数只实用于类。
- 装箱和拆箱:
- 结构体作为值类型,当必要与对象类型交互时,可能会涉及到装箱(boxing)和拆箱(unboxing)操作,这会带来性能开销。
ref struct介绍
在 C# 7.2 及更高版本中,引入了一个新的结构体类型,称为 ref struct。ref struct 是一种特殊的结构体,它有一些独特的特性和限制:
- 堆分配:ref struct 必须在堆上分配,不能在栈上分配。这意味着你不能直接将 ref struct 作为方法参数或返回类型,也不能作为局部变量利用,除非它们是通过引用传递的。
- 引用传递:ref struct 实例必须通过引用传递,即使它们在堆上分配。这包管了对 ref struct 的任何修改都是可见的。
- 不能包含引用类型字段:ref struct 不能包含引用类型的字段,因为它们不能被垃圾采取。这有助于避免不必要的垃圾采取开销,因为 ref struct 不会被垃圾采取器跟踪。
- 不能实现接口:由于 ref struct 必须在堆上分配,它们不能实现接口。这是因为接口的实现可能必要引用类型的特性,这与 ref struct 的设计原则相冲突。
- 不能被继承:ref struct 不能被继承,因为继承可能会引入引用类型的复杂性。
- 利用场景:ref struct 主要用于性能敏感的场景,特殊是在与非托管代码交互时。它们可以用于避免不必要的复制,特殊是在必要频仍传递大型数据结构时。
ref struct 是一种高级特性,通常只在特定的性能优化场景中利用。它们提供了一种方式来避免值类型的一些限制,如栈分配和复制开销,但同时也带来了一些限制,如不能实现接口和不能被继承。在利用 ref struct 时,你必要仔细考虑这些特性和限制,以确保它们适合你的用例。
ref return
在 C# 7.2 引入了 ref return 特性,它允许方法返回对局部变量的引用。这在之前版本的 C# 中是不可能的,因为局部变量在方法调用结束后就不再存在了。通过 ref return,你可以返回一个引用,允许调用者修改返回的局部变量的值。
利用 ref return 时,必要注意以下几点:
- 返回局部变量的引用:ref return 允许方法返回一个引用到局部变量的引用,调用者可以通过这个引用修改原始数据。
- 返回的局部变量必须在方法调用结束后仍然存在:为了满意这个要求,返回的局部变量通常被分配在 stackalloc 内存中,大概作为 ref struct 的成员。
- 调用者可以通过返回的引用修改数据:由于返回的是引用,调用者对返回值的任何修改都会反映在原始数据上。
- 利用 ref 关键字:在方法的返回类型前利用 ref 关键字来指示该方法返回一个引用。
- 调用方法时也必要利用 ref:当调用返回 ref 的方法时,你必要利用 ref 关键字来接收返回的引用。
- 示例:
- private ref int GetCount()
- {
- int count = 1; // 局部变量
- return ref count; // 返回局部变量的引用
- }
-
- void IncrementCount()
- {
- ref int count = ref GetCount();
- count += 1; // 修改原始的局部变量
- }
复制代码
- 利用场景:ref return 可以在必要优化性能的场景中利用,尤其是在避免不必要的数据复制时。例如,在处理大型数据结构或与非托管代码交互时,ref return 可以提供一种有用的方式来传递和修改数据。
- 限制:由于 ref return 改变了局部变量的作用域,因此在利用时必要小心,以避免意外的行为或内存标题。
ref return 是 C# 中一个强盛的特性,它提供了更多的机动性来处理数据的传递和修改。然而,由于它改变了局部变量的生命周期和作用域,因此在现实应用中必要审慎利用。
通过new创建的结构体是放在堆上
在 C# 中,结构体(Struct)通常不必要利用 new 关键字来创建实例。结构体是值类型,它们的实例可以在栈上主动分配,并且可以通过直接赋值来创建。例如:
- Point p = new Point(); // 错误,结构体通常不使用new
- Point p2 = new Point(10, 20); // 正确,但不是推荐的方式
- Point p3 = { X = 10, Y = 20 }; // 推荐的方式,使用初始化器
- Point p4 = new Point { X = 10, Y = 20 }; // 另一种推荐的方式
复制代码 然而,如果你确实想要利用 new 关键字来创建结构体的实例,C# 也允许这样做,但这并不是推荐的做法。利用 new 创建结构体实例时,现实上是在堆上分配内存,这会导致额外的性能开销,因为堆分配必要垃圾采取器的参与。别的,利用 new 创建的值类型实例在某些情况下可能会引起混淆,因为它们的行为更靠近于引用类型。
在大多数情况下,你应该避免利用 new 来创建结构体的实例,而是利用默认构造函数或初始化器来初始化它们。如果你必要在堆上创建结构体的实例,可以利用 Activator.CreateInstance 方法,但这通常不是必要的,除非有特定的需求。
- // 使用Activator.CreateInstance在堆上创建结构体实例
- Point p5 = (Point)Activator.CreateInstance(typeof(Point));
复制代码 总的来说,结构体作为值类型,其设计初志是为了在栈上高效地分配和利用,而不是在堆上。因此,利用 new 创建结构体实例并不是 C# 中的常见做法
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |