zoukankan      html  css  js  c++  java
  • C#篇(三)——函数传参之引用类型和值类型

    首先应该认清楚在C#中只有两种类型:
    1、引用类型(任何称为“类”的类型)
    2、值类型(结构或枚举)

    先来认识一下引用类型和值类型的区别:

    函数传参之引用类型:

    1、先来一个简单的引用类型传参的实例:

    //使用了C#6.0的一个新特性:using static System.Console;
    	class Program
        {
            static void StartTest1(string test)
            {
                test = "test2";
                WriteLine(test);//输出:"test2"
            }
    
            static void StartTest2(string test)
            {
                test = null;
                WriteLine(test);//输出:(空白)
            }
    
            static void Main(string[] args)
            {
                string test = "test1";
                WriteLine(test);//输出:"test1"
    
                StartTest1(test);
                WriteLine(test);//输出:"test1"
    
                StartTest2(test);
                WriteLine(test);//输出:"test1"
            }
        }
    

    输出结果:

    test1
    test2
    test1
    
    test1
    

    结果分析:

    首先明白字符串(string)类型是引用类型,但改变了它的值之后,并没有影响到函数外面那个实参的值,这可能与大家的常识有点相违背,因为我们都知道若是变量以"引用传递"的方式传递,那么调用的方法可以通过更改其参数值,来改变调用者的变量值,但这里有一点需要说明的是:"引用传递"不是等价于引用类型传参,这是很多人的误解的地方。其实在C#当中,引用类型和值类型默认都是以“传值”的方式传递数值(引用)的(引用类型的值就是引用(类似索引或地址),而不是对象本身)。
    请看下图详细分析:
    

    2、再来一个略微复杂的引用类型传参的实例:

     	class Program
        {
            static void StartTest1(StringBuilder test)
            {
                test.Append("test2");
                WriteLine(test);//输出:"test1test2"
            }
    
            static void StartTest2(StringBuilder test)
            {
                test = null;
                WriteLine(test);//输出:(空白)
            }
    
            static void Main(string[] args)
            {
                StringBuilder test = new StringBuilder();
                test.Append("test1");
                WriteLine(test);//输出:"test1"
    
                StartTest1(test);
                WriteLine(test);//输出:"test1test2"
    
                StartTest2(test);
                WriteLine(test);//输出:"test1test2"
                ReadKey();
            }
        }
    

    输出结果:

    test1
    test1test2
    test1test2
    
    test1test2
    

    结果分析:

    StringBuilder和string同样是引用类型,那为什么最终的StringBuilder类型值改变了呢?其实这里要纠正一下,真正改变的不是StringBuilder类型值(也就是引用的值),而是引用指向的字符数组引用指向的对象值改变了。在StringBuilder类里面封装了一个字符数组(最终的输出的就是这个字符数组,而那些操作也是对这个字符数组进行操作)。
    

    结合上面两个实例,对于引用类型传参,从这里可以得出一个小结论:

    1、在函数里面,若直接改变的是引用的值(也就是地址),那么之后的操作都不会影响到函数外面的那个变量
    2、在函数里面,若直接改变的是引用指向的对象(值类型)的值(甚至更深层次的对象的值),那么就会影响到函数外面的变量
    

    所以区分清楚改变的是引用的值还是引用指向的对象(值类型)的值是关键。

    3、再来一个综合的引用类型传参的实例:

        class Program
        {
            class Test
            {
                public int index;//值类型
                public StringBuilder builder;//引用类型
                public string Result{
                    get{return $"{index}:{builder.ToString()}";}
                }
            }
            static void StartTest(Test test)
            {
                test.index++;
                test.builder.Append("test2");
                WriteLine(test.Result);//输出:"2:test1test2"
    
                test.index = new int();
                test.builder = new StringBuilder();
                test.builder.Append("test3");
                WriteLine(test.Result);//输出:"0:test3"
            }
            static void Main(string[] args)
            {
                Test test = new Test {
                    index = 0,
                    builder = new StringBuilder()
                };
                test.index++;
                test.builder.Append("test1");
                WriteLine(test.Result);//输出:"1:test1"
    
                StartTest(test);
                WriteLine(test.Result);//输出:"0:test3"
            }
        }
    

    输出结果:

    1:test1
    2:test1test2
    0:test3
    0:test3
    

    结果分析:

    [若是能够明白1和2中的分析,这个应该没有问题的]

    函数传参之值类型:
    简单的值类型传参这里就不演示了,来一个含有引用类型的值类型传参实例(只是将上例中的struct改为了class,这样好做对比):

        class Program
        {
            struct Test
            {
                public int index;//值类型
                public StringBuilder builder;//引用类型
                public string Result{
                    get{return $"{index}:{builder.ToString()}";}
                }
            }
            static void StartTest(Test test)
            {
                test.index++;
                test.builder.Append("test2");
                WriteLine(test.Result);//输出:"2:test1test2"
    
                test.index = new int();
                test.builder = new StringBuilder();
                test.builder.Append("test3");
                WriteLine(test.Result);//输出:"0:test3"
            }
            static void Main(string[] args)
            {
                Test test = new Test {
                    index = 0,
                    builder = new StringBuilder()
                };
                test.index++;
                test.builder.Append("test1");
                WriteLine(test.Result);//输出:"1:test1"
    
                StartTest(test);
                WriteLine(test.Result);//输出:"1:test1test2"
            }
        }
    

    输出结果:

    1:test1
    2:test1test2
    0:test3
    1:test1test2
    

    结果分析:

    首先应该明白,值类型以"传值"方式传递时,是一种浅拷贝,所以对于引用类型,只是复制了引用的值,副本(形参)中的引用指向的对象还是同一个。其他的自己分析应该明白。
    

    结论:
    1、无论是引用类型还是值类型,永远不会传递对象本身。涉及到一个引用类型时,要么以“引用传递”的方式(使用了ref或out关键字)传递变量,要么以“传值”的方式传递参数值(引用)。所以,通常函数传参(不论是引用类型还是值类型),都是以“传值”的方式传递的,只是要明白引用类型的值是引用本身(相当于一个索引或地址,而这个索引或地址最终指向的才是对象本身)。

    2、“引用方式”传递与“传值”传递方式最大的区别就是“引用方式”要使用ref或out关键字修饰,所以以这个为标准去区分函数传参的方式(而不是以类型是引用类型还是值类型)。

    3、对于传入函数的引用类型变量,最终会不会受到函数内部修改的影响,需要区分清楚函数内部改变的是引用的值还是引用指向的对象(值类型)的值。

  • 相关阅读:
    css3中的位置移动
    css中伪元素选择器
    css中伪类选择器
    html5之属性选择器
    html5的文本属性
    html5的视频和音频使用
    常用的linux命令
    常见的http状态码
    springboot之安装和启动es
    linux版本的jdk安装
  • 原文地址:https://www.cnblogs.com/forcheng/p/6483939.html
Copyright © 2011-2022 走看看