2014-12-29 37 views
2

我有一個現有的方法,如下所示: 方法體有超過1000行代碼,我不能把它帶到這裏,只是在方法體中,如果參數(我的意思是index_param)被改變然後狡猾我得到錯誤的結果。C#:使方法的參數不變的最佳方式

public String calculateValue(int index_param) { 
    //a highly complicated transactional method which returns a String 
} 

我想,以確保該方法操作期間不會改變index_param,並防止它改變參數。 你喜歡這種情況?

+5

如果它是一個參數,它可以被使用,消耗,改變,返回等等。你實際上想要阻止誰改變參數?如果它在方法的內部,那麼你是不是會改變價值的人? –

+1

我不確定這是一個基於瞭解C#如何處理該參數的問題。如果該方法的主體確實改變了index_param值,那麼調用代碼將不會受影響(如果這是您所擔心的)。 – ventaur

+5

然後你的問題是該方法有4500行代碼。一個方法不應該那麼長。分解成更小的組件。 – mason

回答

3

不幸的是,C#沒有局部變量模擬readonly

您可以使用Code Contract Assert需要僞後置條件,雖然我看不到一種方法來避免額外的局部變量,理論上也可能會發生變異。

public String calculateValue(int index_param) { 
    int __DO_NOT_MUTATE_ME = index_param; 

    // a highly complicated transactional method which returns a String 
    // 

    Contract.Assert(index_param == __DO_NOT_MUTATE_ME, "No! Don't change the local var"); 
    return result; 
} 

編輯 - 有些發現
代碼契約不如適合朝向檢測堆棧變量突變如預期。

  • Contract.Ensures()必須在代碼塊的頂部,Ensures僅設計以理智戰勝返回值,而不是本地變量。
  • Contract.Assert因此更清潔,因爲它可以放置在塊中的任何位置。如果堆棧變量在運行時突變,則會拋出。
  • 如果啓用了靜態分析,則還會有一些(後期)編譯時的好處,如附加的圖像(頂部顯示帶有錯誤代碼的警告,底部不顯示沒有突變的警告)。然而,與其標記損壞index_param(用紅色箭頭標記)的錯誤代碼行,不如將合同中的警告標記爲無意義的「考慮添加Contract.Requires(index_param + 1 == index_param);」。嗯...
  • 你可以下載代碼合同插件Visual Studio here - 一個額外的選項卡被添加到屬性對話框。

enter image description here

+0

有趣的建議。如果你使用本地布爾值立即拋出一個異常,這是有道理的。正如你注意到的,沒有例外,這並不能解決原始問題。 –

+1

,這仍然是運行時檢查而不是編譯時間。所以你不會「早失敗」 –

+0

@mason因此相當不名副本。我猜測OP的真正問題更多地與不安全的PInvoke或ComInterop代碼有關,這些代碼可能會無意中影響堆棧。國際海事組織以同樣的方式改變兩個局部變量的可能性很小。 – StuartLC

1

根據評論,如果它是一個參數,它可以被修改。

防止原始值被修改的唯一方法是爲該方法創建一個常量值INSIDE,而不是將其作爲參數注入該方法。

public String calculateValue() { 
    const int index_param = 2; 
} 

第二種選擇是添加只讀專用字段並將您的index_param參數傳遞給封閉類的構造函數。這種方式不能改變你的calculateValue()方法,儘管這需要重構。

public class ContainingClass 
{ 
    private readonly int _index_param; 

    public ContainingClass(int index_param) 
    { 
     _index_param = index_param; 
    } 
} 

這將允許您使用calculateValue內你的私人領域,它不能被改變,這將是一個編譯時檢查。

請注意,這個私人領域仍然可以通過反思來改變。也就是說,如果你有任何人不顧一切地破壞你的代碼庫,那麼你的問題就更大了。

3

該參數由值來傳遞。因此,即使您將變量重新分配給方法主體中的新值,原始引用也不會改變。

在情況下,你不想方法體的變量,甚至重新分配到一個新的價值,像在Java final int index_paramconst int index_param在C++中,有在C#中沒有相應的

+0

這個答案也是正確的,謝謝+1。 – jfun

2

如果你真的偏執,你可以用在確保一個輔助類,你的價值你的價值不會改變

public static class ReadOnly 
{ 
    public static ReadOnly<T> Create<T>(T val) where T : struct 
    { 
     return new ReadOnly<T>(val); 
    } 
} 

public class ReadOnly<T> where T : struct 
{ 
    private readonly T _value; 

    public ReadOnly(T val) 
    { 
     this._value = val; 
    } 

    public T Value { get { return this._value; } } 
} 

public String calculateValue(int index_param) { 
    //a highly complicated transactional method which returns a String 
    var read_only_index_param = ReadOnly.Create(index_param); 
    Console.WriteLine(read_only_index_param.Value); 
} 
0

代碼合同和/或單元測試是最好的解決方案,但另一種是隻需將該參數分配給局部變量並在從該方法返回值之前驗證該參數未更改。如果是這樣,你將不得不拋出一個異常,因爲你的代碼可能是錯誤的。