由几行代码浅析C#的方法参数传递

2024-01-21 23:38

本文主要是介绍由几行代码浅析C#的方法参数传递,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.数据类型

     如果各位已经了解了值类型和引用类型,第一部分可以跳过。

    在了解方法参数之前,我们至少要知道参数都可分为什么样的参数,所以理解C#的数据类型非常重要。C#对其中可用的类型以及其定义有着非常严格的定义。C#的数据类型分为两种:

    a. 值类型;

    b. 引用类型;

     顾名思义,从概念上将,值类型存储的是具体的数值,而引用类型存储的是值的引用,有点类似C中的指针。从存储位置上讲,值类型存储在堆栈中,引用类型存储在托管堆上。

    C#的值类型包括结构体,枚举,还有通常理解下的13个预定义的简单数据类型:整型(8个),浮点(2个),decimal,bool和字符等。

    引用类型包括数组(都存在堆中),类,接口,委托,对象和2个预定义的数据类型:Object和string。

    为了更好的理解引用类型,可以考虑下面的代码:

         

      Student x, y;
x=new Student();
x.value=100;//value是student中的一个静态变量
y=x;
Console.WriteLine(y.value);
y.value=1;
Console.WriteLine(x.value);

      首先我们要理解的重要一点是,在这代码中只有一个对象被创建。X和y都是指向该对象的内存位置,即x和y都是引用型变量,他们有着同一个引用,因此修变x必然会影响到y,反之亦然。因此上面的代码会显示100和1.

    如果你还想更深入,从内存机制了解值类型和引用类型,你可以参考这篇文章《品味类型---值类型与引用类型(上)-内存有理》 

二.方法参数

 

     C#中,是区分函数与方法的正式的C#术语中,“函数成员”不仅包括方法,而且还包括类或结构的一些非数据成员,如索引器,运算符(某错,就是那些加减乘除),构造函数和析构函数,甚至包括属性。而方法的定义包括修饰符,返回值的类型,然后依次是方法名和输入参数的列表(用圆括号括起来)和方法体(用花括号括起来)(详细见《C#高级编程第七版》71页)

    参数传递无非就是传递引用类型或者值类型,在变量传递给方法时,我们通过调用方法理所应当的可以对该变量进行修改,可是真的如此么?

    我们可以看下下面代码:    

static void setValue(int[] iArry,int iInt,string iStr) 
{ 
iArry[0]=10000;
iInt = 10000;
iStr = "10000";
}
static void Main(string[] args)
{
int iInt = 0;
int[] iArry = { 0,1,2,3};
string iStr = "0";
Console.WriteLine("before setValue:");
Console.WriteLine("iInt:" + iInt);
Console.WriteLine("iArry[0]:" + iArry[0]);
Console.WriteLine("iStr:"+iStr);
setValue(iArry, iInt, iStr);
Console.WriteLine("after setValue:");
Console.WriteLine("iInt:" + iInt);
Console.WriteLine("iArry[0]:" + iArry[0]);
Console.WriteLine("iStr:" + iStr);
Console.ReadLine();
}

     在这段代码中iInt为值类型,iArry和iStr同为引用类型,按常规猜测来看,最后的结果有以下四种:

  a.iInt,iArry,iStr分别是0,   0,0(即值类型,引用类型对于参数传递,在这个例子中,方法最终对原来变量均无明显影响);
  b.iInt的值为0,iArry和iStr分别为10000和10000,(即参数传递过程中,函数接受并修改原引用型变量,并不修改原值类型变量);
  c.iInt的值为10000,iArry和iStr均为0和0(即参数传递过程中,函数接受并影响原值型变量,并修改原引用类型变量);
  d.iInt,iArry,iStr分别是10000,   10000,10000(即值类型,引用类型对于参数传递,在这个例子中,方法最终都会对原变量造成影响,);

     按常理来讲,结果应该在上面四种之中,但是程序运行后,却出乎意料,iInt,iArry,iStr的最终值为:0,10000,0.

从结果上看,只有数组的值改变了,int和string的值却没有改变,而string和数组同为引用类型,int为值类型,但方法最终只是影响了原来数组变量。这样的结论有点怪:对于值类型和引用类型的参数,函数似乎不区别引用还是值类型,只是影响特定的类型(例如数组),要是真这样,它又是通过什么影响的呢,难道除了区别引用和值类型还有其他方法?

    我们还可以再进行试验,

     static void setValue(double d,Teacher t) ;(Teacher为自定义类)

     static void setValue(bool b,Teacher t);

     ...........

     ...........

    分别用引用类型和值类型,两两的进行试验,最终我们会发现,所有的值类型最终都没被修改,而引用类型都有修改(除了string),我们似乎可以确定函数会修改原来引类型变量,不会修改原来值类型变量,真的如此,可string为何不会改变呢?

 

    原来,在C#中,方法是通过复制来传递参数的引用型变量只包含对对象的引用,方法复制的是该引用,而不是对象本身,所以对底层对象的修改依旧会保留下来。而对值类型变量,由于其含有实际数据,方法得到的只是该变量的复制,方法对变量所做的任何变量并不影响原来变量。打个例子,就好像两个幼儿园的小朋友看到桌子上有个苹果,使用引用复制的小朋友抱着怀中的苹果玩具不停地咬,无论他怎么咬,都对桌子上的苹果无影响的,而使用值复制的小朋友,直接爬到桌子上抱着苹果吃了起来,这肯定对原苹果是有影响的(例子不好,理解就好^_^)。

    而为什么引用string的就不能修改原值了呢?其实道理很简单,string是一个不可变的数据类型,一旦字符串对象初始化,该字符串对象就不会改变了,表面上修改字符串的方法和运算符实际上是重新创建了一个新的对象,因此在调用方法中对字符串所做的任何改变都不会影响原字符串。(详见《c#高级编程第七版》第三章:对象和类型)原来使用string的小朋友每咬一口苹果,就会凭空变出一个新的苹果让他咬啊……(另外借机推荐小城故事的《运用String的十八层境界》,有转载之嫌疑,故贴上链接)

     面对复杂的数据类型,按引用的效率更高点,因为在按值传递时候,必须进行大量数据的复制,而前面也说过,通过值传递变量是默认的,虽然如此,我们也可以强迫使值参数通过引用传递给方法,为此,就是要使用ref关键字,此时方法对该变量做的任何修改都会影响到原值(那个一咬就会变出一个苹果的string小朋友也不例外)。

 

 

三 小议“学习”能力

    我见过太多人,他们总是强调学习能力。

    为了证明他们强大的学习能力,总是能说出一些新潮的名词或技术,滔滔不绝,好像他们对IT界动向格外的了解。一些看似基础的知识,就像上面讨论的参数,他们可能只是用,从未真正在意过。可要是真正的询问他们有关知识,他们往往就会瞪目结舌,甚至不屑一顾:“这么基础这么简单的不就是这样么!了解会用就好!关键要多多学习一些高深的知识。”没有基础,便向高深所求,结局定然缘木求鱼,上演一出程序员版的“空中楼阁”。

   想起上面推荐的小城故事的《string的十八重境界》那样,或许他写的十八重境界被有些人所轻视,但是这种踏踏实实的精神,不值得我们去欣赏么?

   学习能力,从其本身来讲并无褒贬,浮躁才是最大的致命伤,这才是我们初级程序员要谨记的。(可参见《我在南大的七年》)

 

这篇关于由几行代码浅析C#的方法参数传递的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/631181

相关文章

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

C#下Newtonsoft.Json的具体使用

《C#下Newtonsoft.Json的具体使用》Newtonsoft.Json是一个非常流行的C#JSON序列化和反序列化库,它可以方便地将C#对象转换为JSON格式,或者将JSON数据解析为C#对... 目录安装 Newtonsoft.json基本用法1. 序列化 C# 对象为 JSON2. 反序列化

SpringBoot请求参数传递与接收示例详解

《SpringBoot请求参数传递与接收示例详解》本文给大家介绍SpringBoot请求参数传递与接收示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录I. 基础参数传递i.查询参数(Query Parameters)ii.路径参数(Path Va

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

504 Gateway Timeout网关超时的根源及完美解决方法

《504GatewayTimeout网关超时的根源及完美解决方法》在日常开发和运维过程中,504GatewayTimeout错误是常见的网络问题之一,尤其是在使用反向代理(如Nginx)或... 目录引言为什么会出现 504 错误?1. 探索 504 Gateway Timeout 错误的根源 1.1 后端