前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#篇(三)——函数传参之引用类型和值类型

C#篇(三)——函数传参之引用类型和值类型

作者头像
yaphetsfang
发布2020-07-30 10:01:03
8650
发布2020-07-30 10:01:03
举报
文章被收录于专栏:yaphetsfangyaphetsfang

首先应该认清楚在C#中只有两种类型:

1、引用类型(任何称为“类”的类型) 2、值类型(结构或枚举)

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

函数传参之引用类型:

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

代码语言:javascript
复制
//使用了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"
        }
    }

输出结果:

代码语言:javascript
复制
test1
test2
test1

test1

结果分析:

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

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

代码语言:javascript
复制
    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();
        }
    }

输出结果:

代码语言:javascript
复制
test1
test1test2
test1test2

test1test2

结果分析:

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

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

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

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

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

代码语言:javascript
复制
    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"
        }
    }

输出结果:

代码语言:javascript
复制
1:test1
2:test1test2
0:test3
0:test3

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

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

代码语言:javascript
复制
    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"
        }
    }

输出结果:

代码语言:javascript
复制
1:test1
2:test1test2
0:test3
1:test1test2

结果分析:

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-09-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档