• 技术文章 >后端开发 >C#.Net教程

    .NET框架- in ,out, ref , paras使用的代码总结

    黄舟黄舟2017-03-18 13:45:14原创1774
    C#.net 提供的4个关键字,in,out,ref,paras开发中会经常用到,那么它们如何使用呢? 又有什么区别?

    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未按照预想逻辑被赋值。

    C#.net中有一类TryParse函数,便是out的另一个重要应用。若感兴趣,请见:透过Parse和TryParse:Try-Parse和Tester-Doer模式

    3 ref

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

    例如:

    public void reviseModel(int a)
    {
      a = 12;
    }
    
    Model model = new Model(10);
    //调用reviseModel
    reviseModel(model.a); //model.a仍然=10;by-value
    reviseMode(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被传出。
    见分析文章
    异常设计准则
    透过Parse和TryParse:Try-Parse和Tester-Doer模式
    从文章中看出,相比没有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: TKey
    The 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查找能找到的信息一口气都返回出来,从源头上避免了“两次查找”的冗余操作,有利于程序的性能。

    C#.net中提供了一个关键字 params,以前都不知道有这个关键字,有一次,同事看到我的几版重载函数后,淡定地和我说了一句,哥呀,你可以用params,后来查了查,现在经常用习惯了,这不刚才又把之前写的几版都拿掉了,又用params重构了下。

    5 Paras

    那么,我就把params的用处,我经历的这个过程说一下。

    5.1 问题的需求
    在客户端,客户经常会变动查询的字段,前几天还是根据4个关键字段去服务器查询几个模型呢,今天,又想加1个查询字段。

    根据4个关键字段的查询方法:

            public void GetPlansByInputControl(string planState, string contactno,DatePair dp)
            {       
                string planStat = "";            
                switch (planState)
                {                
                case "...":
                        planStat = "...";                    
                        break;                
                        case "...":
                        planStat = "...";                    
                        break;
                }
                    plans = getPlansWithCondition(Convert.ToDateTime(dp.startValue), Convert.ToDateTime(dp.endValue), planStat, contactno);
    
            }

    调用的getPlansWithCondition方法为

            private List<MPartPlan> getMPartPlansWithCondition(DateTime dateTime, 
            DateTime dateEndTime, string planStat, string contactNo)
            {            var conditions = new CslSqlBaseSingleTable();
                conditions.AddCondition("RequireStartDate", dateTime, DataCompareType.GreaterOrEqual);
                conditions.AddCondition("RequireStartDate", dateEndTime, DataCompareType.LessOrEqual);
                conditions.AddCondition("OrderCode", contactNo, DataCompareType.Equal);            
                if (!string.IsNullOrEmpty(planStat))
                {
                    conditions.AddCondition("PlanState", planStat, DataCompareType.Equal);
                }            
                return _cslMPartPlan.QueryListInSingleTable(typeof(MPartPlan), conditions);
            }
            }

    问题来了,当查询再新加1个字段时,你难道还再重载一个版本吗?

    5.2 应用params

     private List<MPartPlan> getMPartPlansWithCondition(DateTime dateTime, DateTime dateEndTime, string planStat, string contactNo,string newField);

    当C#提供了params后,当然不用,直接将getMPartPlansWithCondition改写为如下

    private List<MPartPlan> getMPartPlansWithCondition(params object[] queryConditions);
    {
       queryConditions[0]
       queryConditions[1]
       queryConditions[2]
       queryConditions[3]
       queryConditions[4]
       //放到字典中dict
    
       sqlQuery(dict);
    }

    以后随意添加查询字段,只要修改下这个函数就行了,不用增删重载版本!!!

    客户端调用,直接加一个字段就行

    _bsl.GetPlansByInputControl(field1, field2,field3,field4,field5);

    5.3 总结

    queryFun(params object[] objs),带有这个参数的函数,只需要一个版本,这样解决了因为个数不一致而导致的多个重载版本,
    在客户端调用时,将属性参数一一列数即可。

    php入门到就业线上直播课:查看学习

    以上就是.NET框架- in ,out, ref , paras使用的代码总结的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    上一篇:.NET框架-引用陷阱的代码实例分享 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• c语言中源文件编译后生成什么文件• c语言标识符有哪些类型• C#中GDI+编程10个基本技巧二• ASP.NET使用Ajax如何返回Json对象的方法具体介绍• 应用绝对路径与相对路径
    1/1

    PHP中文网