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

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti