由几行代码浅析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

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Git中恢复已删除分支的几种方法

《Git中恢复已删除分支的几种方法》:本文主要介绍在Git中恢复已删除分支的几种方法,包括查找提交记录、恢复分支、推送恢复的分支等步骤,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录1. 恢复本地删除的分支场景方法2. 恢复远程删除的分支场景方法3. 恢复未推送的本地删除分支场景方法4. 恢复

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Window Server2016加入AD域的方法步骤

《WindowServer2016加入AD域的方法步骤》:本文主要介绍WindowServer2016加入AD域的方法步骤,包括配置DNS、检测ping通、更改计算机域、输入账号密码、重启服务... 目录一、 准备条件二、配置ServerB加入ServerA的AD域(test.ly)三、查看加入AD域后的变

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭