zoukankan      html  css  js  c++  java
  • C#学习笔记(三):值类型、引用类型及参数传递

    值类型和引用类型简介

    C#中存在两种数据类型,分别是值类型与引用类型,下面我们来看看这两种类型的区别。

    值类型主要包括:

    1. 简单类型(如int、float、char等,注意string不是值类型);
    2. 枚举类型(enum);
    3. 结构体类型(struct);

    引用类型主要包括:

    1. 类类型(如string);
    2. 数组类型(一维或多维数组);
    3. 接口类型(interface);
    4. 委托类型(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关键字来实现:

    1. 使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化;
    2. 使用ref和out时,在方法的参数和执行方法时,都要加ref或out关键字。以满足匹配;
    3. 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
  • 相关阅读:
    5、python中的列表
    Linux---配置新服务器的常见操作(CentOS7)
    MapReduce原理篇
    用MR实现Join逻辑的两种方法
    Linux静态库与共享库
    mysql命令查看表结构及注释
    mysql 数据同步到 elastsearch7 数字类型精度丢失
    canal client-adapter 将mysql数据同步到 es elasticsearch 日期时间少8小时问题解决
    如何用redis做活跃用户统计-HyperLoglog
    jvm 虚拟机内存布局
  • 原文地址:https://www.cnblogs.com/hammerc/p/4608347.html
Copyright © 2011-2022 走看看