首页 > 学院 > 开发设计 > 正文

C#: in ,out, ref 到底有没用的深入分析

2019-11-14 11:57:53
字体:
来源:转载
供稿:网友

C#.net 提供的3个关键字,in,out,ref开发中会经常用到,那么它们3个如何使用呢,又有什么区别,我查了下csdn和知乎的总结,都有点老了,说的也不细致。我尽量通过代码示例写的言简意赅写。

1 in in只用在委托和接口中; 例子:

//测试模型 class Model { public int a { get; set; } public Model(int a) { this.a = a; } }//创建3个实例List<Model> modelList= new List<Model>() { new Model(1), new Model(4), new Model(6) };//调用foreach接口,试着操作3个实例,赋值为nullmodelList.ForEach(e=>e=null); //查看结果://modelList的取值不变。

分析原因,ForEach的参数是委托函数:

//ForEach方法:public void ForEach(Action<T> action);//委托声明:public delegate void Action<in T>(T obj);

委托是泛型的,类型T前加了一个关键字in,因为带有关键字in,所以T obj是不能被修改的。

尝试测试:

//修改元素e的属性amodelList.ForEach(e=>{e.a*=2;});

结果每个元素都乘以2,变为2,8,12。可知,可以修改对象的属性。

2 out

out 关键字用法注意: 1)带有out的形参,在函数定义时,return前必须给函数赋一个值。 2)调用函数时,带有out的参数不必赋一个初始值。 3)out形参传值是通过引用(by reference)

out使用场景: 在函数返回多个值时,通常用out 返回其中一个

public bool Operation(out Model updateMod){ updateMode = new Model(5); try{ // my operation ... // return true; } catch{ //写入日志 return false; }}//使用Model um; //未初始化bool rtnMsg = Operation(out um); //如果初始化,传值通过reference//分析://返回um,如果rntMsg为ture,则um按照预想逻辑被赋值,//如果rntMsg为false 则um未按照预想逻辑被赋值。

3 ref

ref关键字用于改变参数传递,将by value修改为by reference传值,原来是by reference传递的,加上ref还是不加ref,效果是一样的。

例如:

public void reviseModel(int a){ a = 12;}Model model = new Model(10);//调用reviseModelreviseModel(model.a); //model.a仍然=10;by-valuereviseMode(ref model.a); //编译不过,提示ref后的参数不归类与变量int a;reviseMode(ref a); //如果不给变量a赋一个初始值,//编译器也是提示:调用前未被赋值的错误//因此赋值int a= model.a; //变量a初始值为10;reviseMode(ref a);//修改变量a=12;但是model.a的值仍然为10

如何修改对象model中的属性a,将其变为12呢?

//直接将参数设为Model对象,则函数调用时,传值通过by referencepublic void reviseModel(Model md){ md.a = 12;}reviseModel(model );//传值通过by reference

因此,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被传出。 见分析文章 异常设计准则 DateTime.TryParse和Parse 从文章中看出,相比没有out参数的次方法Parse,如果解析字符串失败,则会抛出一个参数错误的异常。

用Try…方法写出来的代码比try…catch写出来的要简洁,于是这也变成了out参数使用的一个常用场景。

2) java和C#比较

在Java里,HashMap

// HashMap<K, V> map;// K key;V val = map.get(key);if (val != null) { // ...}

但val == null,既可能是该map里尚未有键为该key的键值对,也可能是已经有该键值对了但是其值为null。 要区分两者,HashMap提供了containsKey()方法。所以正确的写法是这样的:

// HashMap<K, V> map;// K key;if (map.containsKey(key)) { V val = map.get(key); // ...}

containsKey()跟get()的内部操作几乎是一模一样的,都要做一次hash查找,只是返回了查找结果的不同部分而已。也就是说按照这种“正确写法”来写的话,访问一次HashMap就有双倍开销了。杯具!

C#有许多这种细节设计比Java更贴心。看C#用out关键词如何改进这个问题。

System.Collections.Generic.Dictionary

TryGetValue:Dictionary(TKey, TValue).TryGetValue Method (TKey, TValue) (System.Collections.Generic)public bool TryGetValue( TKey key, out TValue value)ParameterskeyType: TKeyThe key of the value to get.valueType: TValue

利用这个方法,上面的Java代码对应的C#版就可以写成:

// Dictionary<TKey, TValue> dict;// TKey key;TValue val;if (dict.TryGetValue(key, out val)) { // ...}

这就把ContainsKey与Item[Key]的语义结合了起来,把一次hash查找能找到的信息一口气都返回出来,从源头上避免了“两次查找”的冗余操作,有利于程序的性能。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表