zoukankan      html  css  js  c++  java
  • 由几行代码浅析C#的方法参数传递

    一.数据类型 


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

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

          a. 值类型;

          b. 引用类型;

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

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

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

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

             

      1. Student x, y;  
      2.   
      3.   x=new Student();  
      4.   
      5.   x.value=100;//value是student中的一个静态变量   
      6.   
      7.   y=x;  
      8.   
      9.   Console.WriteLine(y.value);  
      10.   
      11.   y.value=1;  
      12.   
      13.   Console.WriteLine(x.value);  

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

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

    二.方法参数


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

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

    我们可以看下下面代码:    

      1. static void setValue(int[] iArry,int iInt,string iStr)   
      2.   
      3.         {   
      4.   
      5.          iArry[0]=10000;  
      6.   
      7.          iInt = 10000;  
      8.   
      9.          iStr = "10000";  
      10.   
      11.         }  
      12.   
      13.    
      14.   
      15.         static void Main(string[] args)  
      16.   
      17.         {  
      18.   
      19.             int iInt = 0;  
      20.   
      21.             int[] iArry = { 0,1,2,3};  
      22.   
      23.             string iStr = "0";  
      24.   
      25.             Console.WriteLine("before setValue:");  
      26.   
      27.             Console.WriteLine("iInt:" + iInt);  
      28.   
      29.             Console.WriteLine("iArry[0]:" + iArry[0]);  
      30.   
      31.             Console.WriteLine("iStr:"+iStr);  
      32.   
      33.    
      34.   
      35.             setValue(iArry, iInt, iStr);  
      36.   
      37.    
      38.   
      39.             Console.WriteLine("after setValue:");  
      40.   
      41.             Console.WriteLine("iInt:" + iInt);  
      42.   
      43.             Console.WriteLine("iArry[0]:" + iArry[0]);  
      44.   
      45.             Console.WriteLine("iStr:" + iStr);  
      46.   
      47.             Console.ReadLine();  
      48.   
      49.         }  

        在这段代码中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的十八重境界》那样,或许他写的十八重境界被有些人所轻视,但是这种踏踏实实的精神,不值得我们去欣赏么?

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

     

  • 相关阅读:
    什么是 bean 的自动装配?
    什么是 Spring 的内部 bean?
    什么是 Spring 的 MVC 框架?
    Spring AOP and AspectJ AOP 有什么区别?
    解释 JDBC 抽象和 DAO 模块?
    volatile 类型变量提供什么保证?
    一个 Spring Bean 定义 包含什么?
    什么是 Spring MVC 框架的控制器?
    使用 Spring 访问 Hibernate 的方法有哪些?
    什么是 Callable 和 Future?
  • 原文地址:https://www.cnblogs.com/songsz1/p/2377948.html
Copyright © 2011-2022 走看看