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

相关文章

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho

Java中Arrays类和Collections类常用方法示例详解

《Java中Arrays类和Collections类常用方法示例详解》本文总结了Java中Arrays和Collections类的常用方法,涵盖数组填充、排序、搜索、复制、列表转换等操作,帮助开发者高... 目录Arrays.fill()相关用法Arrays.toString()Arrays.sort()A

MySQL进行数据库审计的详细步骤和示例代码

《MySQL进行数据库审计的详细步骤和示例代码》数据库审计通过触发器、内置功能及第三方工具记录和监控数据库活动,确保安全、完整与合规,Java代码实现自动化日志记录,整合分析系统提升监控效率,本文给大... 目录一、数据库审计的基本概念二、使用触发器进行数据库审计1. 创建审计表2. 创建触发器三、Java

Nginx安全防护的多种方法

《Nginx安全防护的多种方法》在生产环境中,需要隐藏Nginx的版本号,以避免泄漏Nginx的版本,使攻击者不能针对特定版本进行攻击,下面就来介绍一下Nginx安全防护的方法,感兴趣的可以了解一下... 目录核心安全配置1.编译安装 Nginx2.隐藏版本号3.限制危险请求方法4.请求限制(CC攻击防御)

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

MySQL深分页进行性能优化的常见方法

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将... 目录引言:深分页,真的只是“翻页慢”那么简单吗?一、背景介绍二、深分页的性能问题三、业务场景分析四、

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

C#连接SQL server数据库命令的基本步骤

《C#连接SQLserver数据库命令的基本步骤》文章讲解了连接SQLServer数据库的步骤,包括引入命名空间、构建连接字符串、使用SqlConnection和SqlCommand执行SQL操作,... 目录建议配合使用:如何下载和安装SQL server数据库-CSDN博客1. 引入必要的命名空间2.