C#基础入门之值类型和引用类型的区别详析
作者:.NET开发菜鸟 发布时间:2022-02-22 00:14:04
目录
一、值类型和引用类型的区别
1、赋值时的区别
2、内存分配的区别
3、来自继承结构的区别
二、总结
一、值类型和引用类型的区别
.NET的类型可以分为两类:值类型和引用类型。这两种类型各有特点,即使它们都继承自System.Object,并且有装箱和拆箱等操作确保两种类型可以方便地交互,但是理解值类型和引用类型将有助于程序员编写出高效的代码,相反的,在不理解值类型和引用类型的情况下,程序员很容易编写出可以正确执行但性能较差的代码。
所有.NET的类型都可以分为两类:值类型和引用类型。最简单也最明确的一个区分标准是:所有的值类型都继承自System.ValueType(System.ValueType继承自System.Object),也就是说,所有继承自System.ValueType的类型都是值类型,而其他类型都是引用类型。常用的值类型包括结构、枚举、整数型、浮点型、布尔型等,而在C#中所有以class关键字定义的类型都是引用类型。
1、赋值时的区别
引用类型和值类型最显著的一个区别在于变量的赋值问题。值类型的变量将直接获得一个真实的数据副本,而对引用类型的赋值仅仅是把对象的引用赋给变量,这样就可能导致多个变量引用到一个实际对象实例上。
来看下面一个简单的示例:首先为了测试建立一个简单的引用类型和一个简单的值类型。然后在Main方法中,测试对值类型和引用类型对象进行赋值的不同结果,代码如下:
using System;
namespace ConsoleApp1
{
/// <summary>
/// 一个简单的引用类型
/// </summary>
public class Ref
{
public int iValue { get; set; }
public Ref(int i)
{
iValue = i;
}
public override string ToString()
{
return $"iValue的值为:{iValue.ToString()}";
}
}
/// <summary>
/// 一个简单的值类型
/// </summary>
public struct Val
{
public int Value { get; set; }
public Val(int i)
{
Value = i;
}
public override string ToString()
{
return $"Value的值为:{Value.ToString()}";
}
}
class Program
{
static void Main(string[] args)
{
// 测试引用类型的赋值
Ref ref1 = new Ref(1);
Ref ref2 = ref1;
// 赋值
ref2.iValue = 2;
// 测试值类型的赋值
Val val1 = new Val(1);
Val val2 = val1;
val2.Value = 2;
//输出
Console.WriteLine($"ref1:{ref1}");
Console.WriteLine($"ref2:{ref2}");
Console.WriteLine($"val1:{val1}");
Console.WriteLine($"val2:{val2}");
Console.ReadKey();
}
}
}
简单分析上面的代码,程序定义了一个引用类型Ref和一个值类型Val,两者的内容几乎完全相同。在Main方法中,分别测试了引用类型和值类型的赋值。当代码把一个引用类型变量赋值给另一个引用变量:Ref ref2 = ref1时,实际上是把ref1的对象引用赋给了ref2,这样,两个引用变量实际指向了同一个对象。如图所示:
而值类型的赋值则不同,val1和val2都保留了属于自己的数据副本,所以当val2改变时,val1不受到影响。如图所示:
上面代码的输出结果:
2、内存分配的区别
除了赋值的区别,引用类型和值类型在内存的分配位置上也有区别。引用类型的对象将会在堆上分配内存,而值类型的对象则会在堆栈上分配内存。堆栈的空间相对有限,但运行效率却比高的多。
3、来自继承结构的区别
最后,由于所有的值类型都有一个共同的基类:System.ValueType,所以值类型拥有一些引用类型不具有的共同性质,较重要的一点是值类型的比较方法:Equals方法的实现有了改变。所有的值类型都实现了内容的比较,而引用类型在没有重写Equals方法的情况下,仍然采用引用比较。还是以上面的代码为了,看下面的代码:
using System;
namespace ConsoleApp1
{
/// <summary>
/// 一个简单的引用类型
/// </summary>
public class Ref
{
public int iValue { get; set; }
public Ref(int i)
{
iValue = i;
}
public override string ToString()
{
return $"iValue的值为:{iValue.ToString()}";
}
}
/// <summary>
/// 一个简单的值类型
/// </summary>
public struct Val
{
public int Value { get; set; }
public Val(int i)
{
Value = i;
}
public override string ToString()
{
return $"Value的值为:{Value.ToString()}";
}
}
class Program
{
static void Main(string[] args)
{
//// 测试引用类型的赋值
//Ref ref1 = new Ref(1);
//Ref ref2 = ref1;
//// 赋值
//ref2.iValue = 2;
//// 测试值类型的赋值
//Val val1 = new Val(1);
//Val val2 = val1;
//val2.Value = 2;
//输出
//Console.WriteLine($"ref1:{ref1}");
//Console.WriteLine($"ref2:{ref2}");
//Console.WriteLine($"val1:{val1}");
//Console.WriteLine($"val2:{val2}");
// 测试引用类型的赋值
Ref ref1 = new Ref(1);
Ref ref2 = new Ref(1);
// 测试值类型的赋值
Val val1 = new Val(1);
Val val2 = new Val(1);
Console.WriteLine(ref1.Equals(ref2));
Console.WriteLine(val1.Equals(val2));
Console.ReadKey();
}
}
}
程序输出结果:
在Main方法中,分别定义了一对内容完全相同的值类型对象和引用类型对象,调用Equals方法来比较,发现值类型对象比较返回true,而引用类型对象比较返回false。
二、总结
所有继承自System.ValueType的类型都是值类型,而其他类型都是引用类型。值类型的赋值会产生一个新的数据副本,所以每个值类型都拥有一个数据副本。而引用类型的赋值则是赋值引用。值类型的对象分配在堆栈上,而引用类型的对象分配在堆上。当比较两个值类型时,进行的是内容比较。而比较两个引用类型时,进行的是引用比较。
上面列举的仅仅是值类型和引用类型的一些主要区别,通过这些本质区别,可以产生更多的细节区别,有兴趣的话可以自行研究。
来源:https://www.cnblogs.com/dotnet261010/p/12324317.html


猜你喜欢
- 看到软二的群里,某童鞋发了个自己的java大作业的截图,类似于一个图片,处理后,根据不同的灰度值,填充不同的字符。故,我也用C#来写个玩玩~
- 前言本来是打算用C#爬取天气网站上的信息,然后用正则表达过滤有用信息的,但是很淦,正则表达式太难了。无意间找到添加web引用的方式来获取天气
- 我们在SpringBoot和MyBatis整合的时候,需要在SpringBoot中通过注解方式配置事务回滚1 Pojo类package co
- NPOI是一个开源的C#读写Excel、WORD等微软OLE2组件文档的项目。使用 NPOI 可以在没有安装 Office 或者相应环境的机
- 前言:在java的网络通信中,两个不同节点的主机想要进行通信则可以通过建立Socket对象(相当于客户端主机,向服务端请求发送信息)和Ser
- java模拟银行ATM机操作(基础版),供大家参考,具体内容如下实现的功能需求:修改密码之后,就会自动退出登录,再重新登录,若登录成功才能验
- 本文实例讲述了C#图像边缘检测(Roberts)的方法。分享给大家供大家参考。具体如下://定义roberts算子函数private sta
- 目录I. 环境配置1. 项目配置2. 数据库表II. 参数传递1. @Param注解2. 单参数3. 多参数3. Map传参4. POJO对
- Task的MSDN的描述如下:【Task类的表示单个操作不会返回一个值,通常以异步方式执行。Task对象是一种的中心思想基于任务的异步模式首
- 一、举个栗子public class BeanServiceImpl implements BeanService {}@Configura
- 在项目中遇到需要批量更新的功能,原本想的是在Java中用循环访问数据库去更新,但是心里总觉得这样做会不会太频繁了,太耗费资源了,效率也很低,
- 批量添加,批量更新之前判断是否已经存在批量添加之前判断是否已经存在,foreach separator用UNION ALL。批量
- 本文实例讲述了C#验证码识别基础方法,是非常实用的技巧。分享给大家供大家参考。具体方法分析如下:背景最近有朋友在搞一个东西,已经做的挺不错了
- 之前我们借助一个SuperSocket实现了一个简易版的服务器, 但是不管是Server还是Session都是使用框架的,本篇博客我们要实现
- 一般在web应用中,对客户端提交上来的图片肯定需要进行压缩的。尤其是比较大的图片,如果不经过压缩会导致页面变的很大,打开速度比较慢,当然了如
- 默认日志 Logback :默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台。在运行应用程序和其
- 人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中?这里浅谈Java中的栈和堆首先,将结论写在前
- 网上C#导出Excel的方法有很多。但用来用去感觉不够自动化。于是花了点时间,利用特性做了个比较通用的导出方法。只需要根据实体类,自动导出想
- 用JAVA实现一个简单的电话本管理系统,能存储100个人的姓名、性别、年龄、电话等信息,并能对此系统进行增删改查的操作。部分代码如下:pub
- Java 使用getClass().getResourceAsStream()方法获取资源之前想获取一个资源文件做一些处理,使用getCla