c#的三个高级参数ref out 和Params

前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和Params,在这里我们来分别的讲解一下,在这里的我们先不做具体的解释,我会通过几个例子来做分别的解释。

一:out参数

 1.首先我先给大家一个题:我们来写一个方法,来求一个数组中的最大值,最小值,总和,平均值。代码如下:

 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

通过分析我们会发现如果我们写一个方法的时候,当我们需要返回值的时候,我们只能返回一个值,这个时候我们需要求最大值,最小值,总和和平均值。我们该如何的写呢??假如我们没有学习过out参数,这时候我们可以考虑返回一个数组,并且分别的假定数组中的元素的最大值,最小值,总和。(当总和求出来之后,平均值也就出来了)。详细的代如下:

            //我们声明一个长度为4的数组,假设 res[0] 最大值  res[1]最小值  res[2]总和  res[3]平均值
int[] res = new int[4];
//假定数组中的第一个元素为最大值
res[0] = nums[0];//max
//假定数组中的第二个元素为最小值
res[1] = nums[0];//min
//假定数组中的第三个元素为总和
res[2] = 0;//sum

注:num[0]:这只是我们假定的值。

2.这时候我们需要遍历for循环,如果当前循环到的元素比我的最大值还要大,就把当前的元素赋值给我的最大值,如果当前循环到的元素比我的最小值还要小,就把当前的元素赋值给我的最小值,总和就是所有的元素相加,平均值为总和/数组的长度。详细的代码如下:

            for (int i = 0; i < nums.Length; i++)
{
//如果当前循环到的元素比我假定的最大值还大
if (nums[i] > res[0])
{
//将当前循环到的元素赋值给我的最大值
res[0] = nums[i];
}
//如果当前循环到的元素比我的最小值还要小
if (nums[i] < res[1])
{
//就把当前的元素赋值给我的最小值
res[1] = nums[i];
}
//总和
res[2] += nums[i];
}

3.这时候直接返回数组就行了,完整的代码如下:

 1         /// <summary>
2 /// 计算一个数组的最大值、最小值、总和、平均值
3 /// </summary>
4 /// <param name="nums"></param>
5 /// <returns></returns>
6 public static int[] GetMaxMinSumAvg(int[] nums)
7 {
8 //我们声明一个长度为4的数组,假设 res[0] 最大值 res[1]最小值 res[2]总和 res[3]平均值
9 int[] res = new int[4];
10 //假定数组中的第一个元素为最大值
11 res[0] = nums[0];//max
12 //假定数组中的第二个元素为最小值
13 res[1] = nums[0];//min
14 //假定数组中的第三个元素为总和
15 res[2] = 0;//sum
16 for (int i = 0; i < nums.Length; i++)
17 {
18 //如果当前循环到的元素比我假定的最大值还大
19 if (nums[i] > res[0])
20 {
21 //将当前循环到的元素赋值给我的最大值
22 res[0] = nums[i];
23 }
24 //如果当前循环到的元素比我的最小值还要小
25 if (nums[i] < res[1])
26 {
27 //就把当前的元素赋值给我的最小值
28 res[1] = nums[i];
29 }
30 //总和
31 res[2] += nums[i];
32 }
33 //平均值
34 res[3] = res[2] / nums.Length;
35 return res;
36 }

4.接下来就是在Main方法的中调用,截图如下:

5.这时候我们需要考虑一个问题,我想要在数组中返回bool类型的值或者返回string类型的值,这时候我们怎么办呢??这时候需要我们的out参数登场了,out参数就侧重于在一个方法中返回多个不同类型的值,但是它有一个重要的特点是:必须在方法的内部为其赋值。

(1).在我们需要返回的值的前面加out关键字。

(2).当我们在Main方法中调用我们的方法的时候,首先需要先声明变量。

解题的思路跟上面的一样这里就不做过多的解释了,直接完整的代码奉上:

        /// <summary>
/// 计算一个整数数组的最大值、最小值、平均值、总和
/// </summary>
/// <param name="nums">要求值得数组</param>
/// <param name="max">多余返回的最大值</param>
/// <param name="min">多余返回的最小值</param>
/// <param name="sum">多余返回的总和</param>
/// <param name="avg">多余返回的平均值</param>
public static void Test(int[] nums, out int max, out int min, out int sum, out int avg)
{
//out参数要求在方法的内部必须为其赋值
//我们假设数组中的第一个元素为最大值
max = nums[0];
//假设数组中的第一个元素为最小值
min = nums[0];
//总和设置为零
sum = 0;
for (int i = 0; i < nums.Length; i++)
{
if (nums[i] > max)
{
max = nums[i];
}
if (nums[i] < min)
{
min = nums[i];
}
sum += nums[i];
}
avg = sum / nums.Length;
}
 1         static void Main(string[] args)
2 {
3 //写一个方法 求一个数组中的最大值、最小值、总和、平均值
4 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
5 //变量的声明
6 int max1;
7 int min1;
8 int sum1;
9 int avg1;
10 //方法的调用
11 Test(numbers, out max1, out min1, out sum1, out avg1);
12 Console.WriteLine(max1);
13 Console.WriteLine(min1);
14 Console.WriteLine(sum1);
15 Console.WriteLine(avg1);
16 Console.ReadKey();
17
18 }

调试的结果如下:

6.现在我么用out参数做一个练习:分别的提示用户输入用户名和密码,写一个方法来判断用户输入的是否正确,返回给用户一个登录的结果,并且还要单独的返回给用户一个登录的信息,如果用户名错误,除了返回登录结果之外,还要返回一个“用户名错误”,如果密码错误,除了返回登录结果之外,还要返回一个“密码错误”。

分析的思路:通过上面的题我们分析我们不仅要返回一个登录结果,还要多余的返回一个登录的信息(是登录名错误还是密码错误还是未知的错误),这时候我们需要考虑使用out参数了。完整的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _08使用out参数做登陆
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入用户名");
string userName = Console.ReadLine();
Console.WriteLine("请输入密码");
string userPwd = Console.ReadLine();
string msg;
bool b = IsLogin(userName, userPwd, out msg);
Console.WriteLine("登陆结果{0}",b);
Console.WriteLine("登陆信息{0}",msg);
Console.ReadKey();
} /// <summary>
/// 判断登陆是否成功
/// </summary>
/// <param name="name">用户名</param>
/// <param name="pwd">密码</param>
/// <param name="msg">多余返回的登陆信息</param>
/// <returns>返回登陆结果</returns>
public static bool IsLogin(string name, string pwd, out string msg)
{
if (name == "admin" && pwd == "888888")
{
msg = "登陆成功";
return true;
}
else if (name == "admin")
{
msg = "密码错误";
return false;
}
else if (pwd == "888888")
{
msg = "用户名错误";
return false;
}
else
{
msg = "未知错误";
return false;
}
}
}
}

结果如下截图:

二:那么接下来说一下ref参数的使用。

那么老规矩我们首先来看一下这道题:使用方法来交换两个int类型的变量。

思路分析:首先这个一道非常经典的面试题,我们可以使用两种方法来做这道题(这里不详细的解释)。

首先声明一个方法来实现两个int类型变量的交换。代码如下:

 1         /// <summary>
2 /// 交换两个int类型的变量
3 /// </summary>
4 /// <param name="n1">第一个参数</param>
5 /// <param name="n2">第二个参数</param>
6 public static void Test(int n1, int n2)
7 {
8 n1 = n1 - n2;//-10 20
9 n2 = n1 + n2;//-10 10
10 n1 = n2 - n1;//20 10
11 }

在这个时候考虑我们在Main方法中调用就能实现结果吗??答案是不行的。这就设计到我们的值类型和引用类型(在后面做详细的解释)。这个时候我们就要用到我们的ref参数了。这个时候我们给ref参数做一个定义:ref参数可以将一个变量带入到一个方法中进行改变,改变完成后,在将改变完成后的值带出方法。特点:ref参数要求在方法的外部必须为其赋值,在方法的内部可以不赋值。

完整的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _11_ref练习
{
class Program
{
static void Main(string[] args)
{
//使用方法来交换两个int类型的变量
//必须在方法的外部为其赋值
int n1 = 10;
int n2 = 20;
Test(ref n1, ref n2);
Console.WriteLine(n1);
Console.WriteLine(n2);
Console.ReadKey();
} /// <summary>
/// 交换两个int类型的变量
/// </summary>
/// <param name="n1">第一个参数</param>
/// <param name="n2">第二个参数</param>
public static void Test(ref int n1, ref int n2)
{
//方法的内部可以不赋值
n1 = n1 - n2;//-10 20
n2 = n1 + n2;//-10 10
n1 = n2 - n1;//20 10
}
}
}

代码的结果截图如下:

三:下面我们来介绍一下Params可变参数。

这次我们的题目是:求任意长度的数字的和(整数类型的)。

我们直接给出定义:将实参列表跟可变参数类型一致的元素都当作数组中的元素来处理。特点:Params可变参数必须是形参列表中的最后一个元素。

直接看代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _12params可变参数
{
class Program
{
static void Main(string[] args)
{
int sum = GetSum(8,9);
Console.WriteLine(sum);
Console.ReadKey();
} /// <summary>
/// 求数组中的和
/// </summary>
/// <param name="n">数组</param>
/// <returns>返回总和</returns>
public static int GetSum(params int[] n)
{
int sum = 0;
for (int i = 0; i < n.Length; i++)
{
sum += n[i];
}
return sum;
}
}
}

大家看见了吗,这时候我们将8 ,9 就当作我们形参列表数组中的元素来进行处理,这样的话我们就是实现了我们的功能了。

四:这个时候我们该讨论一下out参数和ref参数为什么可以实现这样的功能。比如拿ref参数做例子:

在讨论这个问题之前我们该说一下值类型和引用类型的区别了。

(1):值类型和引用类型在内存中存储的地方不一样。

(2):在传递值类型和传递引用类型的时候,传递的方式不一样,值类型我们称之为值传递,引用类型我们称之为引用传递。

值类型    :int double bool char decimal struct enum

引用类型:string 自定义类 数组 object 集合 接口

在举例子之前首先请允许我说两个重要的概念:

(1):值类型在复制的时候,传递的是这个值的本身。

(2):引用类型在复制的时候,传递的是对这个对象的引用。

首先我们先来句第一个例子,代码如下:

 //值传递和引用传递
int n1 = 10;
int n2 = n1;
n2 = 20;
Console.WriteLine(n1);
Console.WriteLine(n2);
Console.ReadKey();

大家猜一下结果是多少??答案是:10 20,因为int是值类型,当我声明int n1=10,就相当于我们在内存的栈中开辟了一块内存地址,那我们如何在我们的内存中找到我们的值呢(也就是我们给这块内存区域取一个名字,也就是我们的变量名),ok,当执行int n2=n1的时候,也就是进行了一个赋值的操作,如上所说,值类型在传递的时候我们传递的是值的本身。所以当我们执行完第二行的代码的时候,n2=10,当执行完第三行的代码的时候,又重新给n2赋值n2=20,所有输出的结果就是:n1=10,n2=20。 画图表示如下:

接下来我们看第二个例子(Person默认是自定义类):

1 Person p1 = new Person();
2 p1.Name = "张三";
3 Person p2 = p1;
4 p2.Name = "李四";
5 Console.WriteLine(p1.Name);
6 Console.ReadKey();

这个时候p1.Name为多少呢?? 答案是:李四。首先我们肯定是Person是自定义类,因为我们的自定义类为引用类型,所以在进行引用传递的时候传递的是对这个对象的引用(内存地址)。当我们声明第一行的代码的时候,会在内存的堆中开辟一块区域存储new Person();当为其Name赋值的时候,张三也存储在堆中,这时候也会在栈中开辟一块空间存储了指向这块区域的地址。所以当执行Person p2=p1,是把P1在栈中的引用复制了一份给了P2,所以现在他们两指向的是堆中同一块区域。所以当给Name重新赋值的时候,p1的值也会改变。截图如下:

画图表示如下:

第三个例子:,代码如下:

1 string s1 = "张三";
2 string s2 = s1;
3 s2 = "李四";
4 Console.WriteLine(s1);
5 Console.WriteLine(s2);
6 Console.ReadKey();

这个时候我们的s1和s2是多少呢??经过我的测试答案是:s1:张三,s2:李四。为什么呢?要是按照我们上面的过程应该都是李四呀?因为我们的字符串是非常特殊的,正是由于字符串的不可变性,没每当我们在为其赋值的时候都会在堆中重新开辟一块区域进行存储。所以两块区域没有任何的关系。

思考的问题:这时候我们回顾在一个方法中实现交换变量的例子,当我们没有用ref的时候,传递的是值的本身,当我们使用ref参数的时候传递的是这个对象的引用(内存地址)。所以我们总结出来ref的作用是:ref参数可以将一个值传递改变为引用传递。

好的,我们c#中的三个高级参数在这里就介绍完了,如果大家有什么疑问的话,可以留言,大家共同进步。

原文地址:http://www.cnblogs.com/MoRanQianXiao/p/7702587.html。

浅谈c#的三个高级参数ref out 和Params的更多相关文章

  1. 浅谈c#的三个高级参数ref out 和Params C#中is与as的区别分析 “登陆”与“登录”有何区别 经典SQL语句大全(绝对的经典)

    浅谈c#的三个高级参数ref out 和Params   c#的三个高级参数ref out 和Params 前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和 ...

  2. C# 中的三个高级参数 ref

    今天在浏览博文时,看到这篇文章:C#中的ref 传进出的到底是什么 ? 在传对象时使用ref的疑问 引用类型就传的就是地址,值类型传的就是值,可是还仍有那么多人迷惑,网上虽然流传着很多ref 的相关文 ...

  3. 浅谈Kotlin(三):类

    浅谈Kotlin(一):简介及Android Studio中配置 浅谈Kotlin(二):基本类型.基本语法.代码风格 浅谈Kotlin(三):类 浅谈Kotlin(四):控制流 前言: 已经学习了前 ...

  4. C#高级参数ref的使用

    ref关键字用于将方法内的变量改变后带出方法外.具体我们通过例子来说明: 例子中,将变量n1和n2交换了.如果没有加ref参数,由于没有swadDemo()方法没有返回值,调用后,n1和n2是不会交换 ...

  5. 浅谈WebService开发三(动态调用WebService)转

    在前两讲里,我已经向大家演示了如何使用WebService.同步, 异步调用WebService,而在实际开发过程中,可能会有多个WebService接口供你选择,而在程序执行过程中才决定使用哪一个 ...

  6. 浅谈WCF的三种通信模式:请求响应模式、数据报模式和双工通讯模式

    一: WCF的服务端与客户端在通信时有三种模式:请求响应模式.数据报模式和双工通讯模式. 说一下基本知识,  1.如果想要将当前接口作为wcf服务器,则一定要加上[ServiceContract] 契 ...

  7. 浅谈js函数三种定义方式 & 四种调用方式 & 调用顺序

    在Javascript定义一个函数一般有如下三种方式: 函数关键字(function)语句: function fnMethodName(x){alert(x);} 函数字面量(Function Li ...

  8. 浅谈osi模型 三次握手 四次挥手 ddos攻击原理

    C/S B/S 架构 C:client 端 B:browser 浏览器 S:server 端 C/S架构,基于客户端与服务端之间的通信 例如:QQ,抖音,快手,微信,支付宝等等 优点:个性化设置,响应 ...

  9. 浅谈JavaScript中的变量、参数、作用域和作用域链

    基本类型和引用类型 在JavaScript中有两种数据类型值.基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型值指的是可能由多个值构成的对象.在JavaScript中有5种基本数据类型 ...

随机推荐

  1. IOS内存管理详解

    一.    基本原理 1.        什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空 ...

  2. 11. English vocabulary 英语词汇量

    11. English vocabulary 英语词汇量 (1) The exact number of English words is not known.The large dictionari ...

  3. 让IE8支持html5中的video标签

    这是一篇综合几个前辈的解决方案. 使用video的时候,要遇到的问题. ①不兼容ie9及其以下版本 在<head>里添加两行, 参考张鑫旭前辈的博客,但是在ie8中薄播放. <!-- ...

  4. 第一节 —— vue2.0 环境安装,工程化开发

    vue的开发有两种,一种是直接的在script标签里引入vue.js文件即可,这样子引入的话个人感觉做小型的多页面会比较舒坦,一旦做大型一点的项目,还是离不开webpack. 所以另一种方法也就是基于 ...

  5. pdf预览(pdf.js)

    开门见山,pdf.js是Mozilla(缩写MF或MoFo)全称Mozilla基金会,下面的插件.现在社区非常活跃. Mozilla是为支持和领导开源的Mozilla项目而设立的一个非营利组织 下载地 ...

  6. Beta阶段Scrum 冲刺博客合集

    Beta阶段博客链接集合 第一篇Scrum冲刺博客 第二篇Scrum冲刺博客-Day1 第三篇Scrum冲刺博客-Day2 第四篇Scrum冲刺博客-Day3 第五篇Scrum冲刺博客-Day4 第六 ...

  7. zabbix学习

    snmp 默认监控upd161端口 tcp 也有 [root@bogon ~]# netstat -nlutp|grep snmp tcp 0 0 127.0.0.1:199 0.0.0.0:* LI ...

  8. ASP.NET MVC下使用AngularJs语言(九):日期时间处理与显示

    当在angularjs去显示一个时间时,如原原本本去显示这个值,它将显示一个怪怪的字符串,其实它就是被系列化json之后的字符串.如:一个空值显示为日期时间: 如果非空值显示为日期时间的情形: 为了能 ...

  9. Zhu-Takaoka Two-dimensional Pattern Matching

    Two dimensional pattern matching. Details may be added later.... Corresponding more work can be foun ...

  10. ES6教程-字符串,函数的参数,了解函数的arguments对象,js面向对象,设计模式-单例模式,解构赋值

    前言 主要讲解了ES6对字符串的拓展,包括includes,startsWith和endsWith,另外增加了字符串模板. Start includes()是否包含 startsWith()以什么开头 ...