首頁  >  文章  >  後端開發  >  .NET框架- in ,out, ref , paras使用的程式碼總結

.NET框架- in ,out, ref , paras使用的程式碼總結

黄舟
黄舟原創
2017-03-18 13:45:142384瀏覽

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裡,

Hash#Map

// 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),带有这个参数的函数,只需要一个版本,这样解决了因为个数不一致而导致的多个重载版本,
在客户端调用时,将属性参数一一列数即可。

以上是.NET框架- in ,out, ref , paras使用的程式碼總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn