(教学思路 C#之类三)方法参数类型(ref、out、parmas)

简介:
   这节课我们来学习方法中的参数传递,在面向对象二中,我曾说过,参数也属于变量的一种,在c语言的学习时,同学们都学习过参数这个概念和用法,方法使用参数列表来传递变量的基本语法如下:
returnType  FunctionName(paraType1  paraName1,paraType2  paraName2,……)
{
   Function body;
}
其中的returnType是指方法的返回值,FunctionName指方法的标识符,paraType1指参数1的数据类型,paraName1是参数1的标识符,调用这种类型的方法的时候,必须传入与类型paraType1相符的参数值,并且传入的参数个数也必须相符,方法内部以参数名称paraName1来识别。
     在C#中,参数一共四种形式:分别是
参数类型 值参数
引用参数--ref
输出参数--out
动态参数--parmas

       他们都有什么区别呢?具体怎么用呢?我们现在就开始具体的讲解,
值参数
 
         值参数就是参数的是值类型的,如 int   add(int i,int j){},i和j就是值参数,值参数很好理解,我们也经常用到,下面的代码实例主要是为了对比引用参数和输出参数的区别的实现两个数值交换的例子,请同学们体会它们的不同。
值参数传参实例
 1 //定义一个交换两个数的值的一个方法Exchange 
 2                 //此处请注意,本方法使用了static关键字,原因是:根据我们类一所提到的,如果是实例方法(不加static关键字), 
 3                 //必须通过类的对象来调用,即使在同一个类中,加入static关键字后,静态成员就属于类的引用范围了, 
 4                 //因为在一个类中,所以不必类名.,在其他方法中直接就可以调用这个方法。对比下面的实例方法 
 5                 static  void Exchange( int a,  int b) 
 6                { 
 7                        Console .WriteLine (); 
 8                        Console.WriteLine( "值参数方法进来时,a={0}    b={1}", a, b); 
 9                         int c = 0; 
10                        c = a; 
11                        a = b; 
12                        b = c; 
13                        Console.WriteLine(); 
14                        Console.WriteLine( "经过值参数交换方法交换后,a={0}    b={1}", a, b); 
15                        Console.WriteLine(); 
16 
17                } 
18                 //看这个实例方法,对照上一个方法,看在main方法中的调用是不同的, 
19                 //此方法必须通过Class2的对象s.来调用,而上一个方法是直接调用(因为在一个类中,省略了类名.来调用) 
20                 void Exchange1() 
21                {    
22                } 
23 
24                 static  void Main() 
25                { 
26                         //输入初值 
27                        Console.Write( "请输入整数a="); 
28                         int a =  int.Parse(Console .ReadLine()); 
29                         //输入初值 
30                        Console.Write( "请输入整数b="); 
31                         int b =  int.Parse(Console.ReadLine()); 
32                         
33                         //观察1:经过交换后a和b的结果的变化,(方法结束时交换了a和b的值) 
34                         //2:对比下面的调用,记住静态方法调用的方式 
35                         //3:在main方法结束前,我们再次打印main中定义的a和b的值的变化(和初值相同) 
36                        Exchange(a,b); 
37 
38 
39                         //对比上面的调用,记住实例方法调用的方式 
40                        Class2 s =  new Class2(); 
41                        s.Exchange1(); 
42 
43                         //因为是值参数的传递方法,不会改变Main方法中的a的值,结果应该和初值相同 
44                        Console.WriteLine( "采用值参数交换方法后,Main方法中现在为a="+a); 
45                        Console.WriteLine(); 
46                         //因为是值参数的传递方法,不会改变Main方法中的b的值,结果应该和初值相同 
47                        Console.WriteLine( "经过值参数交换方法后,Main方法中现在为b=" + b); 
48                } 
49 
50                
51        }
 
运行结果
请输入整数a=2
请输入整数b=4
值参数方法进来时,a=2  b=4
经过值参数交换方法交换后,a=4  b=2
采用值参数交换方法后,Main方法中现在为a=2
经过值参数交换方法后,Main方法中现在为b=4
请按任意键继续. . .
 
可以看出值参数经过方法传递后,初值是不改变的,下面我们来看一下引用参数。
引用参数

 
      如果方法的参数为数值类型时,则传递的方式主要分为两种:传值和传址。这两种方式最大的差异在于:当一个参数以传值的方式传递时,变量的值即使在方法中被改变,它本身还是维持一开始传入的值,我们刚才学习的值参数就是传值方式;而以传址方式传入的变量,当方法将其改变的时候,此变量的值便永远被更改,C#默认以传值方式传递参数,如果要使用传址方式传递,就需要用到下面我要说的理论。数值类型一律是以传值方式进行传递的,如果要改变这种行为,用传址方式传递参数,必须使用ref和out关键字进行修饰,当然如果参数本身就是引用类型的(string类型除外),不需要加ref和out关键字,他们的传递也是采用传址方式的.
      使用ref关键字修饰的引用参数传递的方法书写格式如:static  int add(ref int i,ref int j){}  .在参数数值类型的前面加入ref修饰,在调用这个方法时,传入的参数前也必须加ref关键字,如:add(ref a,ref b);
     下面我们上面的例子中Exchange方法中参数b变成ref形式,同学们来体会一下a作为值参数传值传递和b作为引用参数传址传递的区别:
值参数与引用参数的区别
 1class Class2 
 2        { 
 3                 //定义一个交换两个数的值的一个方法Exchange 
 4                 //此处请注意,本方法使用了static关键字,原因是:根据我们类一所提到的,如果是实例方法(不加static关键字), 
 5                 //必须通过类的对象来调用,即使在同一个类中,加入static关键字后,静态成员就属于类的引用范围了, 
 6                 //因为在一个类中,所以不必类名.,在其他方法中直接就可以调用这个方法。对比下面的实例方法 
 7                 static  void Exchange( int a,  ref  int b) 
 8                { 
 9                        Console .WriteLine (); 
10                        Console.WriteLine( "参数进来时,a={0}    b={1}", a, b); 
11                         int c = 0; 
12                        c = a; 
13                        a = b; 
14                        b = c; 
15                        Console.WriteLine(); 
16                        Console.WriteLine( "方法交换后,a={0}    b={1}", a, b); 
17                        Console.WriteLine(); 
18 
19                } 
20                 //看这个实例方法,对照上一个方法,看在main方法中的调用是不同的, 
21                 //此方法必须通过Class2的对象s.来调用,而上一个方法是直接调用(因为在一个类中,省略了类名.来调用) 
22                 void Exchange1() 
23                {    
24                } 
25 
26                 static  void Main() 
27                { 
28                         //输入初值 
29                        Console.Write( "请输入整数a="); 
30                         int a =  int.Parse(Console .ReadLine()); 
31                         //输入初值 
32                        Console.Write( "请输入整数b="); 
33                         int b =  int.Parse(Console.ReadLine()); 
34                         
35                         //观察1:经过交换后a和b的结果的变化,(方法结束时交换了a和b的值) 
36                         //2:对比下面的调用,记住静态方法调用的方式 
37                         //3:main方法结束前,我们再次打印main中定义的a和b的值的变化:a和初值相同,b与出方法值相同 
38                        Exchange(a, ref b); 
39 
40 
41                         //对比上面的调用,记住实例方法调用的方式 
42                        Class2 s =  new Class2(); 
43                        s.Exchange1(); 
44 
45                         //因为是值参数的传递方法,不会改变Main方法中的a的值,结果应该和初值相同 
46                        Console.WriteLine( "采用值参数传递交换方法后,结果应该和初值相同,Main方法中现在为a="+ a); 
47                        Console.WriteLine(); 
48                         //因为是引用ref参数的传递方法,会改变Main方法中的b的值,结果应该和出方法时的值相同 
49                        Console.WriteLine( "经过引用参数传递交换方法后,结果应该和出方法时的值相同,Main方法中现在为b=" + b); 
50                }        
51        }
运行结果
请输入整数a=2
请输入整数b=4
参数进来时,a=2  b=4
方法交换后,a=4  b=2
采用值参数传递交换方法后,结果应该和初值相同,Main方法中现在为a=2
经过引用参数传递交换方法后,结果应该和出方法时的值相同,Main方法中现在为b=2
请按任意键继续. . .
    很明显看出,Main方法中b的值变化了,和出方法时的值相同,而a因为是传值传递,仍然是初值。参数是数值型的传址传递还有一种就是使用out关键字,那么它与ref有什么不同呢?
输出参数
      输出参数传址时,要使用out关键值,out关键字允许我们以一个未初始化的变量作为参数,直接传入方法当中,在方法结束的时候,指定所要返回的值给它,而使用ref关键字传给方法之前,必须将一个数值传入到方法中,out关键字在某种程度上来说,有点像return的功能,就是把方法的结果返回到调用这个方法的主方法中,使用out关键字要注意到:主方法中传入调用的方法的out参数值应该为未初始化的,即使你初始化了,初始化的值也相当于不存在。我们来看这个例子,体会一下out的用法。
比较out和return的用法
 1class Class2 
 2        { 
 3                 //定义一个求正方形面积的方法,传入正方形的边长,返回正方形的面积 
 4                 static  void QMJ( double bc, out     double mj) 
 5                    { 
 6                            mj = bc * bc;                
 7                     } 
 8 
 9                 //对比使用return返回面积的方法,达到的效果是一样的 
10                 static  double returnQMJ( double bc) 
11                { 
12                      return bc * bc; 
13                } 
14                 static  void Main() 
15                { 
16                        Console.Write( "请输入正方形的边长bc="); 
17                         double bc =  double.Parse(Console.ReadLine()); 
18                        Console.WriteLine(); 
19                         //传入QMJ方法的mj是为赋初值的 
20                         double mj; 
21 
22                         //通过方法调用,改变mj的值为出方法的值,打印出来,bc的值不变 
23                        QMJ(bc,  out mj);                         
24                        Console.WriteLine( "正方形的面积mj="+mj); 
25 
26                        Console.WriteLine( "================="); 
27                         //使用return方式,返回方法的结果值 
28                        Console.WriteLine( "使用returnQMJ方法面积mj="+returnQMJ(bc)); 
29                }        
30        }
 
运行结果
请输入正方形的边长bc=4
正方形的面积mj=16
=================
使用returnQMJ方法面积mj=16
请按任意键继续. . .
       这个例子我主要是想告诉大家使用out能够达到和return返回值一样的功能效果。
       下面我们来看一下为什么加了ref和out关键字后,主方法中的数值会改变呢?这主要是因为 存放参数的内存机制发生了变化而引起的。上面的3个例子都是数值类型作为参数,但是因为第二、三例子中加入了ref和out关键字,改变了数值类型按值传递的特性,改为按照传递在内存中存放的地址,按地址传递后参数的值会改变的原因就在此,因为按内存地址传递时,调用方法的参数在定义时,不会因为参数是数值类型而在线程堆栈中开辟新的空间来存放新的值,而是会在线程堆栈中存放一个指向主方法定义的变量(初值)的内存地址,当这个地址指向的内存值因为调用方法中对参数的作用(如交换,如平方)后,内存中的值就改成了调用方法作用后的值,这样在打印主方法的变量时,值自然就改成了调用方法后的值了。
       接下来我们来看一个参数为引用类型的实例,因为参数本身就是引用类型(string类型除外),所以这种参数传递属于传址传递:
数组为参数的传址传递
 1class Class2 
 2        {    
 3                 //定义一个静态的方法,参数是引用类型的数组参数a, 
 4                 //方法内部的功能是,改变数值a中每个元素的值,让改变后的值是原来的2倍 
 5                 static  void ArrayMethod( int [] a) 
 6                { 
 7                        Console.WriteLine( "数值参数传入时,数组中的每个元素值是:"); 
 8                         foreach ( int i  in a) 
 9                        { 
10                                Console.Write(i+ "    "); 
11                        } 
12                        Console.WriteLine(); 
13                         //改变数值a中每个元素的值,让改变后的值是原来的2倍 
14                         for ( int j = 0; j < a.Length; j++) 
15                        { 
16                                a[j] = a[j] * 2; 
17                        } 
18                        Console.WriteLine( "改变后,方法内部的数组a中的每个元素值是:"); 
19                         foreach ( int i  in a) 
20                        { 
21                                Console.Write(i +  "    "); 
22                        } 
23                        Console.WriteLine(); 
24                } 
25             
26                 static  void Main() 
27                { 
28                         //给数值a赋初值 
29                         int[] a =  new  int[] {1,2,3 }; 
30 
31                         //调用方法将引用类型的数组参数a带入方法中 
32                        ArrayMethod(a); 
33 
34                         //经过方法后引用类型的数组参数a的元素值应该发生改变, 
35                        Console.WriteLine( "经过方法后,Main方法中数组a的每个元素值是:"); 
36                         foreach ( int i  in a) 
37                        { 
38                                Console.Write(i +  "    "); 
39                        } 
40                }        
41        }
运行结果:
数值参数传入时,数组中的每个元素值是:
1  2  3
改变后,方法内部的数组a中的每个元素值是:
2  4  6
经过方法后,Main方法中数组a的每个元素值是:
2  4  6  请按任意键继续. . .
      通过这个实例,同学们应该看出引用类型作为参数时,也会影响主方法中带入到调用方法的值。下面我们来学习最后一直参数类型叫做动态传参。
动态传参

 
      动态传参主要是指参数的个数可以根据需要改变,使用动态传参要用到parmas关键字,它的具体用法我们来看这个例子:
动态传参实例1
 1  class Class2 
 2        {    
 3                 //定义一个静态的方法,参数是引用类型的数组参数a,采用动态传参方式, 
 4                 //方法内部的功能是,改变数值a中每个元素的值,让改变后的值是原来的2倍 
 5                 static  void ArrayMethod( params  int [] a) 
 6                { 
 7                        Console.WriteLine( "数值参数传入时,数组中的每个元素值是:"); 
 8                         foreach ( int i  in a) 
 9                        { 
10                                Console.Write(i+ "    "); 
11                        } 
12                        Console.WriteLine(); 
13                         //改变数值a中每个元素的值,让改变后的值是原来的2倍 
14                         for ( int j = 0; j < a.Length; j++) 
15                        { 
16                                a[j] = a[j] * 2; 
17                        } 
18                        Console.WriteLine( "改变后,方法内部的数组a中的每个元素值是:"); 
19                         foreach ( int i  in a) 
20                        { 
21                                Console.Write(i +  "    "); 
22                        } 
23                        Console.WriteLine(); 
24                } 
25             
26                 static  void Main() 
27                { 
28                         //使用了动态传参,我们可以不规定数组的大小,按照需要进行传入。 
29                        ArrayMethod(1,2,3,4); 
30 
31                        /** /////给数值a赋初值 
32                         //int[] a = new int[] { 1, 2, 3 }; 
33 
34                        /** /////调用方法将引用类型的数组参数a带入方法中 
35                         //ArrayMethod(a); 
36 
37                         //经过方法后引用类型的数组参数a的元素值应该发生改变, 
38                         //Console.WriteLine("经过方法后,Main方法中数组a的每个元素值是:"); 
39                         //foreach (int i in a) 
40                         //{ 
41                         //        Console.Write(i + "    "); 
42                         //} 
43                }        
44        }
 对照一下上个例子和本例,我们会发现,动态传参,不必规定数组的个数,而是根据需要使用,本例结果如下:
数值参数传入时,数组中的每个元素值是:
1  2  3  4
改变后,方法内部的数组a中的每个元素值是:
2  4  6  8
请按任意键继续. . .
 
    现在我们来做下一个例子,就是调用方法中有两个参数,一个int型,一个动态的数组类型parmas int[],要告诉大家的是,如果使用parma动态参数,动态参数一定只有一个,必须当有多个参数时,动态参数的位置在最后,我们把上面的例子修改一下,大家体会一下不同。
动态传参实例2
 1class Class2 
 2        {    
 3                 //定义一个静态的方法,参数是引用类型的数组参数a,采用动态传参方式, 
 4                 //方法内部的功能是,改变数值a中每个元素的值,让改变后的值是原来的2倍 
 5                 static  void ArrayMethod( int k, params  int [] a) 
 6                {    
 7                        
 8                        Console.WriteLine( "数值参数传入时,数组中的每个元素值是:"); 
 9                         foreach ( int i  in a) 
10                        { 
11                                Console.Write(i+ "    "); 
12                        } 
13                        Console.WriteLine( "k="+k); 
14                        Console.WriteLine(); 
15                         //改变数值a中每个元素的值,让改变后的值是原来的2倍 
16                         for ( int j = 0; j < a.Length; j++) 
17                        { 
18                                a[j] = a[j] * 2; 
19                        } 
20                        Console.WriteLine( "改变后,方法内部的数组a中的每个元素值是:"); 
21                         foreach ( int i  in a) 
22                        { 
23                                Console.Write(i +  "    "); 
24                        } 
25                        Console.WriteLine( "k="+ k); 
26                        Console.WriteLine(); 
27                } 
28             
29                 static  void Main() 
30                { 
31                         //使用了动态传参,我们可以不规定数组的大小,按照需要进行传入。 
32                         //如果调用方法把parma参数写在前面,因为它的个数是不固定的, 
33                         //她会把最后一个值,也当成是数组中元素进行操作。 
34                        ArrayMethod(1,2,3,4); 
35 
36                     
37                }        
38        }
 
运行结果
数值参数传入时,数组中的每个元素值是:
2  3  4  k=1
改变后,方法内部的数组a中的每个元素值是:
4  6  8  k=1
请按任意键继续. . .
       可以看出因为把parmas的动态参数放到了最后,编译器就自动的把第一个值1直接给了k,把剩下的2,3,4给了数值a参数带入到方法中,所以使用parmas一定要注意到这个问题。
       本节课我们就学习到这里,下一节我们要来学习一下类四:构造方法、析构方法
本文转自叶子文文博客51CTO博客,原文链接http://blog.51cto.com/leafwf/185723如需转载请自行联系原作者

叶子文文
相关文章
|
1月前
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
|
1月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
73 3
|
2月前
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
52 1
|
2月前
|
C#
C#的方法的参数传递
C#的方法的参数传递
28 0
|
7月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
219 3
|
7月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
210 3
|
1月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
36 3
|
5天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
36 12
|
1月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
52 4
|
3月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
53 2