当前位置: 首页 > news >正文

C#中类和结构体的对比

区别

C# 中的类(Class)和结构体(Struct)是两种不同的数据类型,它们在很多方面有相似之处,但也存在一些关键的区别:

  1. 继承

    • 类可以继承其他类或实现接口。

    • 结构体不能继承其他结构体或类,也不能被继承,但可以实现接口。

  2. 默认访问修饰符

    • 类的成员默认是非公开的(private),需要显式指定访问级别。

    • 结构体的所有成员默认是公开的(public),并且不能是私有的。

  3. 实例化

    • 类的实例是在堆上分配的,可以通过 new 关键字来创建。

    • 结构体的实例可以在堆上或栈上分配,但通常在栈上分配,且不需要 new 关键字。

  4. 可变性

    • 类的实例可以是可变的,即其状态可以在创建后被修改。

    • 结构体是值类型,其状态在创建后通常被认为是不可变的,尽管可以通过引用传递来修改其成员。

  5. 垃圾回收

    • 类的实例由垃圾回收器管理,因此涉及到垃圾回收的开销。

    • 结构体由于通常在栈上分配,通常不会有垃圾回收的开销,但当作为对象的一部分或分配在堆上时,其生命周期结束时也需要垃圾回收。

  6. 使用场景

    • 类通常用于表示具有复杂行为的对象,需要继承或多态性的场景。

    • 结构体通常用于表示简单的数据结构,如简单的数据容器或轻量级的数据类型。

  7. 方法和属性

    • 类和结构体都可以有方法、属性、索引器、事件和构造函数。

  8. 静态成员

    • 类和结构体都可以有静态成员,但静态构造函数只适用于类。

  9. 装箱和拆箱

    • 结构体作为值类型,当需要与对象类型交互时,可能会涉及到装箱(boxing)和拆箱(unboxing)操作,这会带来性能开销。

ref struct介绍

在 C# 7.2 及更高版本中,引入了一个新的结构体类型,称为 ref structref struct 是一种特殊的结构体,它有一些独特的特性和限制:

  1. 堆分配ref struct 必须在堆上分配不能在栈上分配。这意味着你不能直接将 ref struct 作为方法参数或返回类型,也不能作为局部变量使用,除非它们是通过引用传递的

  2. 引用传递ref struct 实例必须通过引用传递,即使它们在堆上分配。这保证了对 ref struct 的任何修改都是可见的。

  3. 不能包含引用类型字段ref struct 不能包含引用类型的字段,因为它们不能被垃圾回收。这有助于避免不必要的垃圾回收开销,因为 ref struct 不会被垃圾回收器跟踪。

  4. 不能实现接口:由于 ref struct 必须在堆上分配,它们不能实现接口。这是因为接口的实现可能需要引用类型的特性,这与 ref struct 的设计原则相冲突。

  5. 不能被继承ref struct 不能被继承,因为继承可能会引入引用类型的复杂性。

  6. 使用场景ref struct 主要用于性能敏感的场景,特别是在与非托管代码交互时。它们可以用于避免不必要的复制,特别是在需要频繁传递大型数据结构时。

ref struct 是一种高级特性,通常只在特定的性能优化场景中使用。它们提供了一种方式来避免值类型的一些限制,如栈分配和复制开销,但同时也带来了一些限制,如不能实现接口和不能被继承。在使用 ref struct 时,你需要仔细考虑这些特性和限制,以确保它们适合你的用例。

ref return

在 C# 7.2 引入了 ref return 特性,它允许方法返回对局部变量的引用。这在之前版本的 C# 中是不可能的,因为局部变量在方法调用结束后就不再存在了。通过 ref return,你可以返回一个引用,允许调用者修改返回的局部变量的值。

使用 ref return 时,需要注意以下几点:

  1. 返回局部变量的引用ref return 允许方法返回一个引用到局部变量的引用,调用者可以通过这个引用修改原始数据。

  2. 返回的局部变量必须在方法调用结束后仍然存在:为了满足这个要求,返回的局部变量通常被分配在 stackalloc 内存中,或者作为 ref struct 的成员。

  3. 调用者可以通过返回的引用修改数据:由于返回的是引用,调用者对返回值的任何修改都会反映在原始数据上。

  4. 使用 ref 关键字:在方法的返回类型前使用 ref 关键字来指示该方法返回一个引用。

  5. 调用方法时也需要使用 ref:当调用返回 ref 的方法时,你需要使用 ref 关键字来接收返回的引用。

  6. 示例

    private ref int GetCount()
    {int count = 1; // 局部变量return ref count; // 返回局部变量的引用
    }
    ​
    void IncrementCount()
    {ref int count = ref GetCount();count += 1; // 修改原始的局部变量
    }

  7. 使用场景ref return 可以在需要优化性能的场景中使用,尤其是在避免不必要的数据复制时。例如,在处理大型数据结构或与非托管代码交互时,ref return 可以提供一种有效的方式来传递和修改数据。

  8. 限制:由于 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# 中的常见做法

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Hive中分区(Partition)和分桶(Bucket)区别
  • MBA留学选校中Location的四大考量因素
  • 为呼叫中心创建 SOP 的 10 个好处
  • 优化网络接收缓存减少数据丢包
  • https执行过程,特点,作用
  • 基于深度学习的快速适应任务
  • HarmonyOS应用开发者基础认证,Next版本发布后最新题库
  • 【问题解决方案】npm install报错问题:npm ERR! - 多种解决方案,总有一种可以解决
  • MySQL:CTE 通用表达式
  • 洛克兄弟:E-Bike浪潮下的骑行配件10亿大卖独立站拆解丨出海笔记
  • Android 更换applicationId 后 微信没有回调
  • 【自动化测试工具详解】使用Selenium、JUnit等工具进行自动化测试
  • 漏洞挖掘 | edusrc记一次某中学小程序渗透测试
  • 深入解析汽车VCU:新能源汽车的“大脑”
  • TCP/IP_TCP协议
  • Angular4 模板式表单用法以及验证
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • Bootstrap JS插件Alert源码分析
  • Debian下无root权限使用Python访问Oracle
  • es的写入过程
  • JavaWeb(学习笔记二)
  • JS学习笔记——闭包
  • Logstash 参考指南(目录)
  • MySQL用户中的%到底包不包括localhost?
  • rc-form之最单纯情况
  • Spark学习笔记之相关记录
  • Theano - 导数
  • vue的全局变量和全局拦截请求器
  • 给Prometheus造假数据的方法
  • 技术发展面试
  • 蓝海存储开关机注意事项总结
  • HanLP分词命名实体提取详解
  • ionic异常记录
  • Nginx实现动静分离
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • ​Redis 实现计数器和限速器的
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (二)丶RabbitMQ的六大核心
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (十五)使用Nexus创建Maven私服
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (自适应手机端)行业协会机构网站模板
  • **PHP二维数组遍历时同时赋值
  • **PHP分步表单提交思路(分页表单提交)
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .gitignore文件忽略的内容不生效问题解决
  • .Net Winform开发笔记(一)
  • .NET 药厂业务系统 CPU爆高分析
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NetCore+vue3上传图片 Multipart body length limit 16384 exceeded.