.NET框架- in ,out, ref , paras使用的代码总结 C#中in,out,ref的作用 C#需知--长度可变参数--Params C#中的 具名参数 和 可选参数 DEMO
C#.net 提供的4个关键字,in,out,ref,paras开发中会经常用到,那么它们如何使用呢? 又有什么区别?
1 in
in只用在委托和接口中;
例子:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
分析原因,ForEach的参数是委托函数:
1 |
|
委托是泛型的,类型T前加了一个关键字in,因为带有关键字in,所以T obj是不能被修改的。
尝试测试:
1 |
|
结果每个元素都乘以2,变为2,8,12。可知,可以修改对象的属性。
2 out
out 关键字用法注意:
1)带有out的形参,在函数定义时,return前必须给函数赋一个值。
2)调用函数时,带有out的参数不必赋一个初始值。
3)out形参传值是通过引用(by reference)
out使用场景:
在函数返回多个值时,通常用out 返回其中一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
C#.net中有一类TryParse函数,便是out的另一个重要应用。若感兴趣,请见:透过Parse和TryParse:Try-Parse和Tester-Doer模式
3 ref
ref关键字用于改变参数传递,将by value修改为by reference传值,原来是by reference传递的,加上ref还是不加ref,效果是一样的。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
如何修改对象model中的属性a,将其变为12呢?
1 2 3 4 5 6 |
|
因此,ref关键词使用总结:
ref的话,用于处理值变量,如基本类型、结构等,它们不需要被new出来,传值依照的是值拷贝。
1)ref 后的变量,如果是值类型(value type),那么加上ref后变为按照 by reference传值;
2)ref 后的变量,如果是引用类型(reference type),那么加上ref与不加没有任何区别;
3)ref后的变量,使用前必须赋值
4)ref后的变量不能是引用类型的属性
以上是基本的分析,在使用中就够了,如果想更深入的分析这个问题,请继续。
4 深入探讨out ref
主要分析out ref 到底有何用,不用他们会有什么影响。
1) C#中有一类方法,名字叫作Try…,如Int.TryParse,它返回一个bool值,尝试解析一个字符串,如果成功解析为整数,则返回true,得到的整数作为第二个out的int被传出。
见分析文章
异常设计准则
透过Parse和TryParse:Try-Parse和Tester-Doer模式
从文章中看出,相比没有out参数的次方法Parse,如果解析字符串失败,则会抛出一个参数错误的异常。
用Try…方法写出来的代码比try…catch写出来的要简洁,于是这也变成了out参数使用的一个常用场景。
2) Java和C#比较
1 2 3 4 |
|
但val == null,既可能是该map里尚未有键为该key的键值对,也可能是已经有该键值对了但是其值为null。
要区分两者,HashMap提供了containsKey()方法。所以正确的写法是这样的:
1 2 3 4 |
|
containsKey()跟get()的内部操作几乎是一模一样的,都要做一次hash查找,只是返回了查找结果的不同部分而已。也就是说按照这种“正确写法”来写的话,访问一次HashMap就有双倍开销了。杯具!
C#有许多这种细节设计比Java更贴心。看C#用out关键词如何改进这个问题。
System.Collections.Generic.Dictionary
1 2 3 4 5 6 7 |
|
利用这个方法,上面的Java代码对应的C#版就可以写成:
1 2 3 4 |
|
这就把ContainsKey与Item[Key]的语义结合了起来,把一次hash查找能找到的信息一口气都返回出来,从源头上避免了“两次查找”的冗余操作,有利于程序的性能。
C#.net中提供了一个关键字 params,以前都不知道有这个关键字,有一次,同事看到我的几版重载函数后,淡定地和我说了一句,哥呀,你可以用params,后来查了查,现在经常用习惯了,这不刚才又把之前写的几版都拿掉了,又用params重构了下。
5 Paras
那么,我就把params的用处,我经历的这个过程说一下。
5.1 问题的需求
在客户端,客户经常会变动查询的字段,前几天还是根据4个关键字段去服务器查询几个模型呢,今天,又想加1个查询字段。
根据4个关键字段的查询方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
调用的getPlansWithCondition方法为
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
问题来了,当查询再新加1个字段时,你难道还再重载一个版本吗?
5.2 应用params
1 |
|
当C#提供了params后,当然不用,直接将getMPartPlansWithCondition改写为如下
1 2 3 4 5 6 7 8 9 10 11 |
|
以后随意添加查询字段,只要修改下这个函数就行了,不用增删重载版本!!!
客户端调用,直接加一个字段就行
1 |
|
5.3 总结
queryFun(params object[] objs),带有这个参数的函数,只需要一个版本,这样解决了因为个数不一致而导致的多个重载版本,
在客户端调用时,将属性参数一一列数即可。
C#中in,out,ref的作用
In:过程不会改写In的内容
Out和out:传入的值不会被过程所读取,但过程可以写
ref:传入的值,过程会读,也会写
就象你把布料送到裁缝的一个收料箱(裁缝用这个区别是哪家客户)
IN:这块布料,不能动,我取时还要原样(我取时会要不要这块料,是我自己的事,你管不着,但你不能把这块料做任何改变,你只能看这块料的质地、色彩等等,你要想改变这块料,那自已去照这块料的样子复制一个)
Out和out:我可能给了你布料,也可能没给,也可能我给你的只是一张纸或一块羊皮,但我希望无论我给或没给,你都会给我一件衣服,并放到收料箱中,至于放不放衣服是你的事
ref:这块布料,保证是布料,你可以加工,也可以不加工,但无论你加工或是没加工,都得给我放回收料箱中.
ref
通常我们向方法中传递的是值.方法获得的是这些值的一个拷贝,然后使用这些拷贝,当方法运行完毕后,这些拷贝将被丢弃,而原来的值不将受到影响.此外我们还有其他向方法传递参数的形式,引用(ref)和输出(out).
有时,我们需要改变原来变量中的值,这时,我们可以向方法传递变量的引用,而不是变量的值.引用是一个变量,他可以访问原来变量的值,修改引用将修改原来变量的值.变量的值存储在内存中,可以创建一个引用,他指向变量在内存中的位置.当引用被修改时,修改的是内存中的值,因此变量的值可以将被修改.当我们调用一个含有引用参数的方法时,方法中的参数将指向被传递给方法的相应变量,因此,我们会明白,为什么当修改参数变量的修改也将导致原来变量的值.
创建参数按引用传递的方法,需使用关键字ref.例;
using System;
class gump
{
public double square(ref double x)
{
x=x*x;
return x;
}
}
class TestApp
{
public static void Main()
{
gump doit=new gump();
double a=3;
double b=0;
Console.WriteLine(\"Before square->a={0},b={1}\",a,b);
b=doit.square(ref a);
Console.WriteLine(\"After square->a={0},b={1}\",a,b);
}
}
通过测试,我们发现,a的值已经被修改为9了.
out
通过指定返回类型,可以从方法返回一个值,有时候(也许还没遇到,但是我们应该有这么个方法),需要返回多个值,虽然我们可以使用ref来完成,但是C#专门提供了一个属性类型,关键字为out.介绍完后,我们将说明ref和out的区别.
通过使用out关键字,我们改变了三个变量的值,也就是说out是从方法中传出值.
using System;
class gump
{
public void math_routines(double x,out double half,out double squared,out double cubed)
//可以是:public void math_routines(//ref double x,out double half,out double squared,out double cubed)
//但是,不可以这样:public void math_routines(out double x,out double half,out double squared,out double cubed),对本例来说,因为输出的值要靠x赋值,所以x不能再为输出值
{
half=x/2;
squared=x*x;
cubed=x*x*x;
}
}
class TestApp
{
public static void Main()
{
gump doit=new gump();
double x1=600;
double half1=0;
double squared1=0;
double cubed1=0;
[Page]
/*
double x1=600;
double half1;
double squared1;
double cubed1;
*/
Console.WriteLine(\"Before method->x1={0}\",x1);
Console.WriteLine(\"half1={0}\",half1); Console.WriteLine(\"squared1={0}\",squared1);
Console.WriteLine(\"cubed1={0}\",cubed1);
doit.math_rountines(x1,out half1,out squared1,out cubed1);
Console.WriteLine(\"After method->x1={0}\",x1);
Console.WriteLine(\"half1={0}\",half1);
Console.WriteLine(\"squared1={0}\",squared1);
Console.WriteLine(\"cubed1={0}\",cubed1);
}
}
我们发现,ref和out似乎可以实现相同的功能.因为都可以改变传递到方法中的变量的值.但是,二者本质本质的区别就是,ref是传入值,out是传出值.在含有out关键字的方法中,变量必须由方法参数中不含out(可以是ref)的变量赋值或者由全局(即方法可以使用的该方法外部变量)变量赋值,out的宗旨是保证每一个传出变量都必须被赋值.
上面代码中被/**/注释掉的部分,可以直接使用.也就是说,在调用方法前可以不初始化变量.但是\"x1\"是要赋值的,否则要报错.而ref参数,在传递给方法时,就已经是还有值的了,所以ref侧重修改.out侧重输出.
---------------------
作者:随智阔
来源:CSDN
原文:https://blog.csdn.net/zhongguowangzhan/article/details/78551729
版权声明:本文为博主原创文章,转载请附上博文链接!
重拾C#日常积累:in、ref、out类型标识的方法参数
为不具有 in、ref 或 out 的方法声明的参数会按值传递给调用的方法。 可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改后的值。 可以通过使用方法参数关键字更改此行为。
本部分介绍声明方法参数时可以使用的关键字:
params 指定此参数采用可变数量的参数。
in 指定此参数由引用传递,但只由调用方法读取。
ref 指定此参数由引用传递,可能由调用方法读取或写入。
out 指定此参数由引用传递,由调用方法写入。
参考地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/method-parameters
C#需知--长度可变参数--Params
Params用于参数的数量可变的情况下,即参数的个数是未知数。
使用Params需要知道以下几点:
1、如果函数传递的参数含有多个,使用Params标记的参数数组需要放在最后
图上显示的很明确,不需要多解释,只能使用A的那种排序方式
2、Params修饰的一定要是数组,而且必须是一维数组
3、Params不能和ref、out组合使用
具体参见Hunts.C前辈的文章http://www.cnblogs.com/hunts/archive/2007/01/13/619620.html
4、与Params修饰的参数数组对应的实参可以是同一类型的数组名(注意:只能是一个数组名,多个数组名是不可以的),也可以是任意多个与该数组的元素属于同一类型的变量
演示代码
class Program { static void Main(string[] args) { //展示参数是可变的 int i = Sum(1, 13, 23, 34); Console.WriteLine(i); int j = Sum(1, 1, 3, 2, 4, 4, 44, 555, 6); Console.WriteLine(j); //实参可以是数组名 int[] ArrayI = new int[5] { 1, 2, 3, 4, 5 }; int ArraySum = Sum(ArrayI); Console.WriteLine(ArraySum); Console.Read(); } static int Sum(params int[] s) { int sum = 0; foreach(int i in s) { sum += i; } return sum; } }
C#中的 具名参数 和 可选参数
具名参数 和 可选参数 是 C# framework 4.0 出来的新特性。
一. 常规方法定义及调用
public void Demo1(string x, int y) { //do something... } public void Main() { //调用 Demo1("similar", 22); }
调用时,参数顺序(类型)必须与声明一致,且不可省略。
二. 可选参数的声明及调用
可选参数分为两种情况: 1. 部分参数可选; 2. 全部参数都是可选
//部分可选(x为必选,y为可选) public void Demo2(string x, int y = 5) { //do something... } public void Main() { //调用 Demo2("similar"); // y不传入实参时,y使用默认值5 Demo2("similar", 10); // y传入实参,则使用实参10 }
注: 当参数为部分可选时, 可选参数 的声明必须定义在 不可选参数 的后面(如上: y 的声明在 x 之后),不然会出现如下错误提示:
//全部可选(x,y 均为可选参数) public void Demo3(string x = "demo", int y = 5) { //do something... } public void Main() { //调用 Demo3(); // x,y不传入实参时,x,y使用默认值 "demo",5 Demo3("similar"); // y不传入实参时,y使用默认值5 Demo3("similar", 10); // x,y都传入实参 }
注: a. 当参数全部都为可选时,参数的声明顺序可以随意定义,不分先后。
b. 参数声明定义可以无顺序,但调用时必须与声明时的一致。
上面的调用只写的3种,其实还有一种,就是 x 使用默认值,y 传入实参,即 : Demo3(10);
但这样调用会报错,因为Demo3的第一个参数是 string 类型,错误消息如图:
但是现在我只想传入y, 不想传入 x ,该怎么办呢,那么就要用到 C#的 具名参数。
三. 具名参数
具名参数的使用主要是体现在函数调用的时候。
public void Main() { //调用 Demo3(); // x,y不传入实参时,x,y使用默认值 "demo",5 Demo3("similar"); // y不传入实参时,y使用默认值5 Demo3("similar", 10); // x,y都传入实参 // 具名参数的使用 Demo3(y:10); }
通过具名参数,我们可以指定特定参数的值,这里我们通过 Demo3(y:10)就可以解决我们上面遇到的问题(x使用默认值,y使用实参)。
注: 当使用 具名参数时,调用方法可以不用管参数的声明顺序,即如下调用方式也是可以的:
在调用含有可选参数的方法时,vs中会有智能提示,提示哪些是可以选参数及其默认值,中括号表示可选[]:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Cin只读0ref有去有回0out无去有0params可变数量 { //测试模型 class Model { public int a { get; set; } public Model(int a) { this.a = a; } } class Program { #region in 关键字 delegate void DContravariant<in A>(A argumen); static void objFunction(object obj) { Console.WriteLine("你变了"); } static void strFunction(string str) { Console.WriteLine(str); } #endregion static void Main(string[] args) { //in只用在委托和接口中; List<Model> modelList = ), ), ) }; modelList.ForEach(e => e = null); Console.WriteLine(); //modelList的取值不变。ForEach的参数是委托函数: //委托是泛型的,类型T前加了一个关键字in,因为带有关键字in,所以T obj是不能被修改的。 //修改 属性a modelList.ForEach(e => { e.a *= ; }); Console.WriteLine();//结果每个元素都乘以2,变为2,8,12。可知,可以修改对象的属性。 #region in关键字 // in关键字可指定类型参数是逆变的。 // 逆变使你使用的类型可以比泛型参数指定的类型派生程度更小。 DContravariant<object> f1 = objFunction; //DContravariant<string> f1 = objFunction; // 可以使用string类型参数传入in object DContravariant<string> f2 = strFunction; f2 = f1; // string类型可以向object转换。 f2("执行了"); #endregion Console.WriteLine("----"); string refss = "yuanli"; //有进有出 Getref(ref refss); Console.WriteLine(refss); Console.WriteLine("----"); //string outss = null; //string outss = "yuanli"; //赋不赋值 无所谓 方法里面也取不到(取的话 提示错误 使用了未赋值的 out 参数) //代码规范严谨 建议不赋 string outss;// 值为null 和第一种是一样的 Getout(out outss); Console.WriteLine(outss); // Getparams("); //一般方法 顺序一致 不可省略 demo(); //可选参数 demokexuan(); // 不是 重载方法 demokexuan("similar"); // 没为第二个参数赋值 设了默认值 //Demo3引出具名 Demo3引出具名(); Demo3引出具名("similar"); Demo3引出具名(); //Demo3引出具名(10); //只想x 使用默认值,y 传入实参 引出具名 报错 最匹配的重载方法具有一些无效参数 //无法从“int”转换为“string” // 具名参数的使用 Demo3引出具名(y: ); //注: 当使用 具名参数时,调用方法可以不用管参数的声明顺序,即如下调用方式也是可以的: [] 表示可选 Demo3引出具名(x: ); Demo3引出具名(y: , x: "); //一般方法 同样可以使用具名参数调用方式调用 demo(str: ); demo(i: , str: "); } static void Getref(ref string str) { Console.WriteLine("str是什么 " + str); str = "ref修改str"; } static void Getout(out string str) {//out //Console.WriteLine("str是什么 " + str); // 错误 使用了未赋值的 out 参数“str” str = "out赋值str"; Console.WriteLine(str); } static void Getparams(params string[] str) {//params foreach (var item in str) { Console.WriteLine(item); } } static void demo(string str, int i) {// } ) {//kexuan 设置默认值 } //当参数为部分可选时, 可选参数 的声明必须定义在 不可选参数 的后面 //static void demokexuan2(string str = "", int y) //错误 1 可选参数必须出现在所有必需的参数之后 ) {//kexuan 设置默认值 } //引出具名参数 ) { //do something... } } }
.NET框架- in ,out, ref , paras使用的代码总结 C#中in,out,ref的作用 C#需知--长度可变参数--Params C#中的 具名参数 和 可选参数 DEMO的更多相关文章
- C#中out和ref之间的区别【转】
首先:两者都是按地址传递的,使用后都将改变原来参数的数值. 其次:ref可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个数值从out传递进去的,out进去后,参数的数值为空,所 ...
- C#中out和ref之间的区别
首先:两者都是按地址传递的,使用后都将改变原来参数的数值. 其次:rel可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个数值从out传递进去的,out进去后,参数的数值为空,所 ...
- c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)
一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会 ...
- C#中in,out,ref,params的作用和区别
ref和out的区别在C# 中,既可以通过值也可以通过引用传递参数.通过引用传递参数允许函数成员更改参数的值,并保持该更改.若要通过引用传递参数, 可使用ref或out关键字.ref和out这两个关键 ...
- 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)
写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...
- C# 中out,ref,params参数的使用
C#中有三个高级参数,分别是out,ref,params: 1.out参数 方法使用return 只能返回一个值(一个数值或一个指针值),out参数可以帮助我们在一个方法中返回多个值,不限类型. ...
- 使用XWAF框架(1)——Web项目的代码分层
建议在Eclipse环境下使用XWAF框架来开发用户的Web项目,并遵循以下步骤和约定. 1.获取XWAF框架压缩包文件 程序员点击下列地址免费下载XWAF框架的压缩包文件:XWAF框架压缩文件 2. ...
- react native中state和ref的使用
react native中state和ref的使用 因props是只读的,页面中需要交互的情况我们就需要用到state. 一.如何使用state 1:初始化state 第一种方式: construct ...
- [转] C#中out和ref之间的区别
gskcc 的原文地址 C#中out和ref之间的区别 首先:两者都是按地址传递的,使用后都将改变原来参数的数值. 其次:ref可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个 ...
随机推荐
- FPGA LVDS I/O as an Analog Programmable Comparator
http://www.eetimes.com/author.asp?section_id=36&doc_id=1320289 Seeing the new ADC IP being bandi ...
- Install Linux Kernel 4.10 In CentOS and Ubuntu
https://www.ostechnix.com/install-linux-kernel-4-10-centos-ubuntu/
- SharePoint Server 2013 Offline Installation (without Internet)
转自:http://social.msdn.microsoft.com/Forums/sharepoint/zh-CN/08f90e0f-1f52-4eba-9f6e-4dd635ffaadc/sha ...
- FIS3配置fis-conf.js
设置规则的配置接口: fis.match(selector, props); 1.添加md5戳:对 js.css.png 图片引用 URL 添加 md5 戳: fis.match('*.{js,css ...
- 仿LOL项目开发第一天
---恢复内容开始--- 仿LOL项目开发第一天 by---草帽 项目源码研究群:539117825 最近看了一个类似LOL的源码,颇有心得,所以今天呢,我们就来自己开发一个类似于LOL的游戏demo ...
- 3月题外:关于JS实现图片缩略图效果的一个小问题
由于3月可能要结束实习,所以应该不会有特别固定的主题,另外我会在月初陆续补上上个月的番外篇Projection和TMS,作为介绍性的内容对矢量切片部分进行补充,剩下时间不定期写一些杂烩. 最近两天在做 ...
- BIG5, GB(GB2312, GBK, ...), Unicode编码, UTF8, WideChar, MultiByte, Char说明与区别
汉语unicode编译方式,BIG5是繁体规范,GB是简体规范 GB是大陆使用的国标码,BIG5码,又叫大五码,是台湾使用的繁体码. BIG5编码, GB编码(GB2312, GBK, ...), U ...
- 7.volatile关键字
volatile:一个线程修改了某一个共享变量的值,其他线程也是否能够立即知道这个修改的 1.主要是让该“变量”在多个线程中可见,在java中每一个线程都有一块自己的工作区,其中就存放着所有线程“共享 ...
- 如何用 js 递归输出树型
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- (剑指Offer)面试题8:旋转数组的最小数字
题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...