[你必须知道的.NET]第十二回:参数之惑---传递的艺术(下)

本文将介绍以下内容:

  • 按值传递与按引用传递深论
  • ref和out比较 
  • 参数应用浅析 

 

接上篇继续,『第十一回:参数之惑---传递的艺术(上)

4.2 引用类型参数的按值传递

当传递的参数为引用类型时,传递和操作的是指向对象的引用,这意味着方法操作可以改变原来的对象,但是值得思考的是该引用或者说指针本身还是按值传递的。因此,我们在此必须清楚的了解以下两个最根本的问题:

  • 引用类型参数的按值传递和按引用传递的区别?
  • string类型作为特殊的引用类型,在按值传递时表现的特殊性又如何解释?

首先,我们从基本的理解入手来了解引用类型参数按值传递的本质所在,简单的说对象作为参数传递时,执行的是对对象地址的拷贝,操作的是该拷贝地址。这在本质上和值类型参数按值传递是相同的,都是按值传递。不同的是值类型的“值”为类型实例,而引用类型的“值”为引用地址。因此,如果参数为引用类型时,在调用方代码中,可以改变引用的指向, 从而使得原对象的指向发生改变,如例所示: 

引用类型参数的按值传递
// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/01 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com

using System;

namespace Anytao.net.My_Must_net
{
    class Args
    {
        public static void Main()
        {
            ArgsByRef abf = new ArgsByRef();
            AddRef(abf);
            Console.WriteLine(abf.i);
        }

        private static void AddRef(ArgsByRef abf)
        {
            abf.i = 20;
            Console.WriteLine(abf.i);
        }

    }

    class ArgsByRef
    {
        public int i = 10;
    }
}


因此,我们进一步可以总结为:按值传递的实质的是传递值,不同的是这个值在值类型和引用类型的表现是不同的:参数为值类型时,“值”为实例本身,因此传递的是实例拷贝,不会对原来的实例产生影响;参数为引用类型时,“值”为对象引用,因此传递的是引用地址拷贝,会改变原来对象的引用指向,这是二者在统一概念上的表现区别,理解了本质也就抓住了根源。关于值类型和引用类型的概念可以参考《第八回:品味类型---值类型与引用类型(上)-内存有理》《第九回:品味类型---值类型与引用类型(中)-规则无边》《第十回:品味类型---值类型与引用类型(下)-应用征途》,相信可以通过对系列中的值类型与引用类型的3篇的理解,加深对参数传递之惑的昭雪。

了解了引用类型参数按值传递的实质,我们有必要再引入另一个参数传递的概念,那就是:按引用传递,通常称为引用参数。这二者的本质区别可以小结为:

  • 引用类型参数的按值传递,传递的是参数本身的值,也就是上面提到的对象的引用;
  • 按引用传递,传递的不是参数本身的值,而是参数的地址。如果参数为值类型,则传递的是该值类型的地址;如果参数为引用类型,则传递的是对象引用的地址。

关于引用参数的详细概念,我们马上就展开来讨论,不过还是先分析一下string类型的特殊性,究竟特殊在哪里?

关于string的讨论,在本人拙作《第九回:品味类型---值类型与引用类型(中)-规则无边》已经有了讨论,也就是开篇陈述的本文成文的历史,所以在上述分析的基础上,我认为应该更能对第九回的问题,做以更正。

string本身为引用类型,因此从本文的分析中可知,对于形如

static void ShowInfo(string aStr){...}

的传递形式,可以清楚的知道这是按值传递,也就是本文总结的引用类型参数的按值传递。因此,传递的是aStr对象的值,也就是aStr引用指针。接下来我们看看下面的示例来分析,为什么string类型在传递时表现出特殊性及其产生的原因?

// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/05 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com
using System;

namespace Anytao.net.My_Must_net
{
    class how2str
    {
        static void Main()
        {
            string str = "Old String";
            ChangeStr(str);
            Console.WriteLine(str);
        }

        static void ChangeStr(string aStr)
        {
            aStr = "Changing String";
            Console.WriteLine(aStr);
        }
    }
}

下面对上述示例的执行过程简要分析一下:首先,string str = "Old String"产生了一个新的string对象,如图表示:

 

然后执行ChangeStr(aStr),也就是进行引用类型参数的按值传递,我们强调说这里传递的是引用类型的引用值,也就是地址指针;然后调用ChangeStr方法,过程aStr = "Changing String"完成了以下的操作,先在新的一个地址生成一个string对象,该新对象的值为"Changing String",引用地址为0x06赋给参数aStr,因此会改变aStr的指向,但是并没有改变原来方法外str的引用地址,执行过程可以表示为:

 

因此执行结果就可想而知,我们从分析过程就可以发现string作为引用类型,在按值传递过程中和其他引用类型是一样的。如果需要完成ChangeStr()调用后,改变原来str的值,就必须使用ref或者out修饰符,按照按引用传递的方式来进行就可以了,届时aStr = "Changing String"改变的是str的引用,也就改变了str的指向,具体的分析希望大家通过接下来的按引用传递的揭密之后,可以自行分析。

4.3 按引用传递之ref和out

不管是值类型还是引用类型,按引用传递必须以ref或者out关键字来修饰,其规则是:

  • 方法定义和方法调用必须同时显示的使用ref或者out,否则将导致编译错误;
  • CRL允许通过out或者ref参数来重载方法,例如: 
// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/03 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com

using System;
namespace Anytao.net.My_Must_net._11_Args
{
    class TestRefAndOut
    {
        static void ShowInfo(string str)
        {
            Console.WriteLine(str);
        }
        static void ShowInfo(ref string str)
        {
            Console.WriteLine(str);
        }
    }
}

当然,按引用传递时,不管参数是值类型还是引用类型,在本质上也是相同的,这就是:ref和out关键字将告诉编译器,方法传递的是参数地址,而不是参数本身。理解了这一点也就抓住了按引用传递的本质,因此根据这一本质结论我们可以得出以下更明白的说法,这就是:

  • 不管参数本身是值类型还是引用类型,按引用传递时,传递的是参数的地址,也就是实例的指针。
  • 如果参数是值类型,则按引用传递时,传递的是值类型变量的引用,因此在效果上类似于引用类型参数的按值传递方式,其实质可以分析为:值类型的按引用传递方式,实现的是对值类型参数实例的直接操作,方法调用方为该实例分配内存,而被调用方法操作该内存,也就是值类型的地址;而引用类型参数的按值传递方式,实现的是对引用类型的“值”引用指针的操作。例如:  
// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/06 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com
using System;

namespace Anytao.net.My_Must_net
{
    class TestArgs
    {
        static void Main(string[] args)
        {
            int i = 100;
            string str = "One";
            ChangeByValue(ref i);
            ChangeByRef(ref str);
            Console.WriteLine(i);
            Console.WriteLine(str);
        }

        static void ChangeByValue(ref int iVlaue)
        {
            iVlaue = 200;
        }

        static void ChangeByRef(ref string sValue)
        {
            sValue = "One more.";
        }
    }
}

如果参数是引用类型,则按引用传递时,传递的是引用的引用而不是引用本身,类似于指针的指针概念。示例只需将上述string传递示例中的ChangeStr加上ref修饰即可。 

下面我们再进一步对ref和out的区别做以交代,就基本阐述清楚了按引用传递的精要所在,可以总结为:

  • 相同点:从CRL角度来说,ref和out都是指示编译器传递实例指针,在表现行为上是相同的。最能证明的示例是,CRL允许通过ref和out来实现方法重载,但是又不允许通过区分ref和out来实现方法重载,因此从编译角度来看,不管是ref还是out,编译之后的代码是完全相同的。例如:
// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/03 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com

using System;
namespace Anytao.net.My_Must_net._11_Args
{
    class TestRefAndOut
    {
        static void ShowInfo(string str)
        {
            Console.WriteLine(str);
        }
        static void ShowInfo(ref string str)
        {
            Console.WriteLine(str);
        }
        static void ShowInfo(out string str)
        {
            str = "Hello, anytao.";
            Console.WriteLine(str);
        }
    }
}

编译器将提示: “ShowInfo”不能定义仅在 ref 和 out 上有差别的重载方法。 

  • 不同点:使用的机制不同。ref要求传递之前的参数必须首先显示初始化,而out不需要。也就是说,使用ref的参数必须是一个实际的对象,而不能指向null;而使用out的参数可以接受指向null的对象,然后在调用方法内部必须完成对象的实体化。  

5. 结论

完成了对值类型与引用类型的论述,在这些知识积累的基础上,本文期望通过深入的论述来进一步的分享参数传递的艺术,解开层层疑惑的面纱。从探讨问题的角度来说,参数传递的种种误区其实根植与对值类型和引用类型的本质理解上,因此完成了对类型问题的探讨再进入参数传递的迷宫,我们才会更加游刃有余。我想,这种探讨问题的方式,也正是我们追逐问题的方式,深入进入.NET的高级殿堂是绕不开这一选择的。

 

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)David Chappell, Understanding .NET

 

温故知新

[开篇有益]
[第一回:恩怨情仇:is和as]
[第二回:对抽象编程:接口和抽象类]
[第三回:历史纠葛:特性和属性]
[第四回:后来居上:class和struct]
[第五回:深入浅出关键字---把new说透]
[第六回:深入浅出关键字---base和this]
[第七回:品味类型---从通用类型系统开始]
[第八回:品味类型---值类型与引用类型(上)-内存有理]
[第九回:品味类型---值类型与引用类型(中)-规则无边]
[第十回:品味类型---值类型与引用类型(下)-应用征途]
[第十一回:参数之惑---传递的艺术(上)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438745.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[你必须知道的.NET]第十三回:从Hello, world开始认识IL

本文将介绍以下内容: IL代码分析方法 Hello, world历史 .NET学习方法论1. 引言 1988年Brian W. Kernighan和Dennis M. Ritchie合著了软件史上的经典巨著《The C programming Language》,我推荐所有的程序人都有机会重温这本历史上的经典之作。从那时起…

[你必须知道的.NET]第十四回:认识IL代码---从开始到现在

本文将介绍以下内容: IL代码分析方法 IL命令解析 .NET学习方法论 1. 引言 自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持,给予了anytao巨大的鼓励和动力。俱往昔,我发现很多的园友都把…

[你必须知道的.NET]第十五回:继承本质论

本文将介绍以下内容: 什么是继承?继承的实现本质1. 引言 关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。 本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的…

海岸鸿蒙2018年标准物质,海岸鸿蒙——20年权威的标准物质研制单位

摘要:海岸鸿蒙创办于1996年,是专业从事国家标准物质研发、生产、销售的高新技术企业。海岸鸿蒙创办于1996年,是专业从事国家标准物质研发、生产、销售的高新技术企业。海岸鸿蒙自创办以来,一直秉持“以市场为导向,以科…

[你必须知道的.NET]第十六回:深入浅出关键字---using全接触

本文将介绍以下内容: using指令的多种用法using语句在Dispose模式中的应用1. 引言 在.NET大家庭中,有不少的关键字承担了多种角色,例如new关键字就身兼数职,除了能够创建对象,在继承体系中隐藏基类成员,还在…

华为双系统是鸿蒙系统吗,华为p50pro是鸿蒙系统吗-华为p50pro有双系统吗

华为p50pro的外观基本上就是延续了上一代的风格,没有什么太大的变化,不过影像能力还是非常令人期待的,下面一起来了解华为p50pro的系统方面,看看有没有你暂所不知的消息。近日,有消息曝光了华为 P50 Pro将会有 Harmony…

[你必须知道的.NET]第十七回:貌合神离:覆写和重载

本文将介绍以下内容: 什么是覆写,什么是重载覆写与重载的区别覆写与重载在多态特性中的应用1. 引言 覆写(override)与重载(overload),是成就.NET面向对象多态特性的基本技术之一,两…

鸿蒙系统正式开源,余承东:鸿蒙系统正式开源,友商也可以使用!

鸿蒙OS2.0正式开源:从PPT走向前台,该谁脸红了?在9月10日的开发者大会上,华为鸿蒙2.0发布,已经不再是某些人嘴中的PPT、又哄又蒙的鸿蒙了。说鸿蒙是PPT的言论,在前不久还能看到,如今鸿蒙已经发布…

[你必须知道的.NET]第十八回:对象创建始末(上)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究1. 引言 了解.NET的内存管理机制,首先应该从内存分配开始,也就是对象的创建环节。对象的创建,是个复杂的过程,主要包括内存分配和初始化两个环节。例如&…

[你必须知道的.NET]第十九回:对象创建始末(下)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例分配于托管堆上,而线…

android material 颜色值,Android Material Colors 谷歌 Material Design 标准颜色

Android Material Colors谷歌 Material Design 标准颜色。调色板资源文件通过 doc-getter 自动抓取生成。运行 Demo 来查看效果。UsageGradlecompile com.takwolf.android.materialdesign:color:0.0.1Stylecolor/material_indigo_500color/material_indigo_700color/material_p…

突然吐字不清_要注意说话吐字不清小心是脑中风前兆

任何疾病发病之前往往会有一些前兆出现,像是脑中风这种疾病在发作之前也是有前兆的,若是朋友们能够尽早的发现就能够在发病前进行治疗了。朋友们要注意的是说话吐字不清小心是脑中风前兆,这是脑中风发病前的典型前兆,还有头晕、呕…

c++ vs release没有exe_未来安全 | 第一次Geant4培训总结 | 有没有你关注的问题呢?...

Geant4简介Geant4是蒙卡工具包,模拟很多粒子,记录一些统计量,用这些统计量去估计真实的物理实验的结果。蒙卡模拟程序,从最老的MCNP,到PENELOPE,FLUKA等。MCNP是用输入卡片(输入文件)实现的,在一…

[你必须知道的.NET]第二十一回:认识全面的null

说在,开篇之前 null、nullable、??运算符、null object模式,这些闪亮的概念在你眼前晃动,我们有理由相信“存在即合理”,事实上,null不光合理,而且重要。本文,从null的基本认知开始&#xff0…

html用表格做个人主页页面,利用HTML的表格进行页面布局

在DIVCSS布局出现前,基本上所用的网站都使用table来进行布局。因为table布局很简单,但是table布局不灵活且代码很多。下面将介绍怎样使用table来进行布局。实例:我们来布局一个常见网站后台程序的架构。布局图如下所示:实例代码&a…

cesium坡度坡向分析_景观设计分析图制作技巧到底是什么?

国外设计中,人们都开始用动态分析图啦厉害的不要不要啊!如果你也想做如此高逼格的分析图记得往下看!景观设计分析为:人文,背景,区位,现状,历史,功能,流线&…

采购模板html5,蓝色的采购信息管理系统手机界面wap模板

手机版大气信息管理系统界面模板,采购信息管理wap手机模板下载。资源下载此资源下载价格为4D币,请先登录资源文件列表codedown123-080801-25/business_log.html , 6657codedown123-080801-25/choose.html , 6869codedown123-080801-25/css/animate.css ,…

adobe audition cs6 能打开mpcm文件吗?_Adobe全家桶出现这些漏洞,赶紧上官网下载补丁吧...

导语:Adobe已发布了计划的2020年7月安全更新,涵盖了五个不同产品领域的缺陷:Creative Cloud Desktop;媒体编码器;下载管理器; 真正的服务;和ColdFusion。其中四个错误的严重性被评为严重,而其他…

详解CSS的相对定位和绝对定位

CSS的相对定位和绝对定位 一、Static 静态定位 通常情况下,我们元素的position属性的值默认为static 就是没有定位,元素出现在正常的文档流中,这个时候你给这个元素设置的left,right,bottom,top这些偏移属性都是没有效果的,不会生…

观看实验中微型计算机虚拟拆装演示,虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0...

下面我们对虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0文件阐述相关使用资料和虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0文件的更新信息。虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0“微机原理虚拟仿真实验”适用于《微机原理》《微…