C#学习笔记(三):值类型、引用类型及参数传递
值类型和引用类型简介
C#中存在两种数据类型,分别是值类型与引用类型,下面我们来看看这两种类型的区别。
值类型主要包括:
- 简单类型(如int、float、char等,注意string不是值类型);
- 枚举类型(enum);
- 结构体类型(struct);
引用类型主要包括:
- 类类型(如string);
- 数组类型(一维或多维数组);
- 接口类型(interface);
- 委托类型(delegate);
内存分布
值类型的实例大部分情况下会被存放在线程的堆栈上,由操作系统管理其在内存上的分配和释放。
引用类型的实例都会被存放在托管堆上,由垃圾回收器(GC)管理其在内存上的分配和释放。
1 namespace Study 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 int valueType = 0; 8 string refType = "abc"; 9 } 10 } 11 }
上面的示例中:
valueType是值类型,其变量名和值都会被存储到堆栈中。
refType是引用类型,其变量名任然存储在堆栈中,但是其值"abc"是存储在托管堆中的。
引用类型中嵌套定义值类型的情况
1 namespace Study 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Test test = new Test(); 8 } 9 } 10 11 public class Test 12 { 13 private int valueType = 0; 14 } 15 }
上例中的Test作为引用类型被分配到托管堆中,其属性valueType虽然是值类型,但是仍然也会被分配到托管堆中。
总结
- 值类型继承自ValueType,ValueType继承自Object;引用类型继承自Object;
- 值类型不由GC控制释放,其作用域结束时会被操作系统自动回收;引用类型需要由GC控制其内存释放;
- 值类型不能被设置为null;
装箱和拆箱
装箱是指值类型转换为引用类型的过程,拆箱表示将引用类型转换为值类型的过程。
装箱和拆箱堆性能有一定影响,所以我们需要需要尽量避免。
参数传递
形参值方法中定义的参数,实参指实际传递给方法的参数。
值类型参数按值传递
形参作为从实参拷贝的一个副本,对形参的修改不会影响到实参。
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int num = 100; 10 Func(num); 11 12 Console.WriteLine("num: " + num); 13 14 Console.Read(); 15 } 16 17 private static void Func(int i) 18 { 19 i++; 20 21 Console.WriteLine("i: " + i); 22 } 23 } 24 }
结果如下:
1 i: 101 2 num: 100
引用类型参数按值传递
形参作为从实参拷贝的一个副本,但是由于是引用类型记录的是内存地址,所以两个参数实际都指向托管堆中的同一个对象,故对形参的修改会影响到实参。
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Test t = new Test(); 10 t.num = 50; 11 Func(t); 12 13 Console.WriteLine("Main: " + t.num); 14 15 Console.Read(); 16 } 17 18 private static void Func(Test test) 19 { 20 test.num = 100; 21 22 Console.WriteLine("Func: " + test.num); 23 } 24 } 25 26 public class Test 27 { 28 public int num = 0; 29 } 30 }
结果如下:
1 Func: 100 2 Main: 100
string引用类型按值传递的特殊情况
string对象虽然是引用类型,但是由于string类型具备的不变性,在传递参数后实际上在内存中重新拷贝了一份数据。
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 string str = "old string"; 10 Func(str); 11 12 Console.WriteLine("Main: " + str); 13 14 Console.Read(); 15 } 16 17 private static void Func(string s) 18 { 19 s += ", new string"; 20 21 Console.WriteLine("Func: " + s); 22 } 23 } 24 }
结果如下:
1 Func: old string, new string 2 Main: old string
值类型和引用类型参数按引用传递
当需要直接传递参数的引用到函数内部时,通过引用传递参数允许函数成员更改参数的值,并保持该更改,需要使用ref和out关键字来实现:
- 使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化;
- 使用ref和out时,在方法的参数和执行方法时,都要加ref或out关键字。以满足匹配;
- out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候;
下面我们看一个例子:
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int num = 23; 10 FuncInt(ref num); 11 Console.WriteLine("MainInt: " + num); 12 13 string str = "old string"; 14 FuncStr(ref str); 15 Console.WriteLine("MainStr: " + str); 16 17 Console.Read(); 18 } 19 20 private static void FuncInt(ref int i) 21 { 22 i += 100; 23 24 Console.WriteLine("FuncInt: " + i); 25 } 26 27 private static void FuncStr(ref string s) 28 { 29 s += ", new string"; 30 31 Console.WriteLine("FuncStr: " + s); 32 } 33 } 34 }
结果如下:
1 FuncInt: 123 2 MainInt: 123 3 FuncStr: old string, new string 4 MainStr: old string, new string
如果是out关键字则不能初始化实参,如下:
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int num; 10 FuncInt(out num); 11 Console.WriteLine("MainInt: " + num); 12 13 string str; 14 FuncStr(out str); 15 Console.WriteLine("MainStr: " + str); 16 17 Console.Read(); 18 } 19 20 private static void FuncInt(out int i) 21 { 22 i = 100; 23 24 Console.WriteLine("FuncInt: " + i); 25 } 26 27 private static void FuncStr(out string s) 28 { 29 s = ", new string"; 30 31 Console.WriteLine("FuncStr: " + s); 32 } 33 } 34 }
结果如下:
1 FuncInt: 100 2 MainInt: 100 3 FuncStr: , new string 4 MainStr: , new string
C#学习笔记(三):值类型、引用类型及参数传递相关推荐
- ABP学习笔记: C#值类型和引用类型
出现问题的场景:我在写c#时候在构造器的参数选项里写了xxxx(string?name),提示错误. 只有值类型才可以加问号,引用类型本身就是可以为空,可以不加问号. 于是找了资料记录一下. 1.简单 ...
- [读书笔记]C#学习笔记三: C#类型详解..
前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. ...
- C# 值类型 引用类型 作为参数传递区别
C#数据类型分类: http://msdn.microsoft.com/zh-cn/library/t63sy5hs(v=VS.80).aspx. 值类型作为参数传递: http://msdn.mic ...
- Python基础学习笔记三
Python基础学习笔记三 print和import print可以用,分割变量来输出 import copy import copy as co from copy import deepcopy ...
- MYSQL学习笔记三:日期和时间函数
MYSQL学习笔记三:日期和时间函数 1. 获取当前日期的函数和获取当前时间的函数 /*获取当前日期的函数和获取当前时间的函数.将日期以'YYYY-MM-DD'或者'YYYYMMDD'格式返回 */ ...
- Go圣经-学习笔记之复合类型(二)
2019独角兽企业重金招聘Python工程师标准>>> 上一篇 Go圣经-学习笔记之复合类型 下一篇 Go圣经-学习笔记之复合数据结构(三) map介绍和简单使用 map是一种无序的 ...
- python3常用模块_Python学习笔记三(常用模块)
Python 学习笔记三 (常用模块) 1.os模块 os模块包装了不同操作系统的通用接口,使用户在不同操作系统下,可以使用相同的函数接口,返回相同结构的结果. os.name:返回当前操作系统名称( ...
- K8S 学习笔记三 核心技术 Helm nfs prometheus grafana 高可用集群部署 容器部署流程
K8S 学习笔记三 核心技术 2.13 Helm 2.13.1 Helm 引入 2.13.2 使用 Helm 可以解决哪些问题 2.13.3 Helm 概述 2.13.4 Helm 的 3 个重要概念 ...
- Spring框架学习笔记(三)(AOP,事务管理)
Spring框架学习笔记(三) 九.AOP 9.1 AOP的注解配置 (1) 新建计算器核心功能(模拟:不能在改动核心代码) (2) 建立一个普通的Java类写增强代码(面向切面编程),使用Sprin ...
- SurfaceFlinger学习笔记(三)之SurfaceFlinger进程
概述 本系列是基于android Q 即android10 SurfaceFlinger学习笔记(一)应用启动流程 SurfaceFlinger学习笔记(二)之Surface SurfaceFling ...
最新文章
- python循环最简单的解释_python循环-通往简洁的必经之路
- python3.8 新特性
- Fiddler模拟Slow Network
- 【CSS3】自定义滚动条样式 -webkit-scrollbar
- 揭秘基于MBSE集成化的汽车电子解决方案
- GTK+ tutorial
- iOS导航控制器和Segues
- 用EasyRecovery怎么恢复电脑中已删除的视频
- 八月份技术指标和个人指标
- 正在做鸡肋事情的你,准备好了奔赴下一个战场么?
- come back 继工作1年1一个月随想
- ExtendSim高效 易学 易用 真正能用起来的系统仿真与优化工具
- 2018年我的开发管理总结
- 慎重决定!从自建服务器到选择阿里云
- 全面风险管理体系架构图
- python判断是否有重复单词_Python判断两个单词的相似度
- H5手机休闲游戏开发商有哪些?带你看遍北京游戏研发公司
- 安卓手机变Linux服务器丨AidLux上手体验
- 秋招/春招常见笔试题目——二叉树系列(C/C++)
- xshell常用命令和快捷键----(备忘、随时更新)
热门文章
- zlog日志库的简单封装,以及给debug级别添加颜色显示
- javascript 异步队列
- Java开发笔记(一百五十)C3P0连接池的用法
- 黑马程序员_java基础笔记(09)...HTML基本知识、CSS、JavaScript、DOM
- 【汇总目录】嵌入式系统技术
- 【原创】大叔经验分享(11)python引入模块报错ImportError: No module named pandas numpy...
- android122 zhihuibeijing 新闻中心NewsCenterPager加载网络数据实现
- WPF下通过附加属性实现单实例启动
- 使用java.text包格式化数字和日期
- [翻译]Review——How JavaScript works:The building blocks of Web Workers