C#開發人員應該知道的13件事情

77rou發表於2017-02-22

本文講述了C#開發人員應該瞭解到的13件事情,希望對C#開發人員有所幫助。

1. 開發過程

開發過程是錯誤和缺陷開始的地方。使用工具可以幫助你在釋出之後,解決掉一些問題。

編碼標準

遵照編碼標準可以編寫出更多可維護的程式碼,特別是在由多個開發人員或團隊編寫和維護的程式碼庫中。例如FxCop,StyleCop和ReSharper等,就是常用的實施編碼標準的工具。

開發人員:在壓縮程式碼之前,請使用工具仔細檢查是否違反了標準,並且對結果進行分析。使用工具發現的程式碼路徑問題,不比你預期的少。

程式碼審查

程式碼審查和結對程式設計是任務開發人員審查他人編寫的原始碼的常見做法。透過這些方式希望能夠檢查出作者的錯誤,如編碼錯誤或實現錯誤。

程式碼審查是一個很有價值的做法,但是它依賴於人類,易犯錯誤,所以很難擴充套件。

靜態分析

靜態分析工具會在不執行程式碼的情況下分析程式碼,在不需要編寫測試用例的情況下,查詢違反編碼標準或存在缺陷的問題。它們能有效地找到問題,但你需要選擇出那些能夠定位出有價值問題的工具,找出有價值的問題。C#靜態分析工具包括Coverity,CAT.NET和Visual Studio程式碼分析。

動態分析

動態分析工具在執行時分析程式碼,幫助你查詢缺陷,如安全漏洞,效能和併發問題。它分析執行時環境的上下文中的程式碼,因此其有效性受測試工作負載的限制。Visual Studio提供了一些動態分析工具,包括併發視覺化器,IntelliTrace和分析工具。

管理人員/團隊領導:利用開發最佳實踐,以避免常見的陷阱。仔細考慮可用的工具,以確保它們與你的需求和文化相容。

測試

有許多型別的測試,例如:單元測試,系統整合測試,效能測試,滲透測試。在開發階段,大多數測試由開發人員或測試人員編寫,以驗證應用程式是否滿足其要求。

測試僅在它們執行正確的程式碼時有效。在實現功能和測試的同時,保持開發速度是具有挑戰性的。

開發最佳實踐

投入時間來識別和配置工具,以便找到你關心的程式碼問題,無需為開發人員帶來更多的工作。經常自動執行分析工具和測試,以確保開發人員在程式碼剛寫完不久,就能定位到問題。

儘快地定位到所有的診斷輸出 - 無論是編譯器警告,標準違例,透過靜態分析識別的缺陷,還是測試失敗。如果新的診斷全部是可忽略的,那麼審查所起的作用就增加了,開發人員也不必再為程式碼問題煩惱。

採用這些最佳實踐有助於提高程式碼的質量,安全性和可維護性,開發人員的一致性和生產力以及釋出的可預測性。

關心 工具 影響
一致性,可維護性 編碼標準,靜態分析,程式碼審查 一致的間距,命名和格式化提高了可讀性,並使開發人員更容易編寫和維護程式碼。
正確性 程式碼審查,靜態分析,動態分析,測試 程式碼不僅需要在語法上有效,而且必須按照開發人員的意圖並滿足專案需求。
功能 測試 測試驗證程式碼是否滿足要求,如正確性,可擴充套件性,魯棒性和安全性。
安全 編碼標準,程式碼審查,靜態分析,動態分析,測試 安全是一個非常複雜的問題; 任何弱點或缺陷都可能被利用。
開發人員生產力 編碼標準,靜態分析,測試 當他們有工具來識別錯誤時,開發人員更快地實現程式碼更改。
釋放可預測性 編碼標準,程式碼審查,靜態分析,動態分析,測試 簡化後期活動,儘早解決缺陷和問題,儘可能縮短修復週期。


2. 型別陷阱

C#的一個主要優勢是其靈活的型別系統; 型別安全有助於早期發現錯誤。透過強制實施嚴格的型別規則,編譯器能夠幫助你保持正確的編碼實踐。C#語言和.NET框架提供了豐富的型別集合以適應最常見的需求。大多數開發人員很好地瞭解常見的型別及其用途,但有一些常見的誤解和誤用。

有關.NET Framework類庫的更多資訊可以在MSDN庫中找到。

瞭解和使用標準介面

某些介面涉及常用的C#特性。例如,IDisposable允許使用常用的資源處理習語,例如“using”關鍵字。理解什麼時候使用介面,能夠使你編寫更容易維護的C#程式碼。

避免ICloneable - 設計者從來沒有明確複製的物件是深複製還是淺複製。由於沒有正確複製物件行為的標準,也就無法有效的使用這樣的介面。

結構

儘量避免寫到結構體。將它們視為不可變的,能夠防止混淆的發生,並且在共享記憶體的場景(如多執行緒應用程式)下更安全。相反,在建立結構體時使用初始化物件,如果需要更改值,則建立新的例項。

要了解哪些標準型別/方法是不可變的並返回新值(例如,string,DateTime)和哪些是可變的(List.Enumerator)。

字串

字串可以為null,因此在適當時,使用起來很方便。等價(s.Length == 0)可能會丟擲一個NullReferenceException,但是String.IsNullOrEmpty(s)和String.IsNullOrWhitespace(s)函式能夠優雅地處理null。

標記列舉

列舉型別和常量值是能表露出自己含義的識別符號,用於替換魔術數字,以便使得程式碼更加可讀。

如果你發現需要建立列舉的集合,標記列舉可能是一個更簡單的選擇:

[Flag] public enum Tag {
  None =0x0,
  Tip =0x1,
  Example=0x2 }

這使你能夠輕鬆地為程式碼段新增多個標籤:

snippet.Tag = Tag.Tip | Tag.Example

這可以改善資料封裝,因為你不必擔心透過Tag property getter暴露內部集合。

等價比較

有兩種型別的等價:

  1. 引用相等,這意味著兩個引用,引用了同一個物件。
  2. 值平等,這意味著兩個不同的物件是等值的。

此外,C#提供了多種方法來測試等價。最常見的方法是使用:

  • ==和!=運算子
  • 繼承自Object的虛擬Equals方法
  • 靜態Object.Equals方法
  • IEquatable介面的Equals方法
  • 靜態Object.ReferenceEquals方法

可能難以知道預期的是引用相等還是值相等。如果你重寫Equals,不要忘記IEquatable ,GetHashCode(),如MSDN中所述。

注意無型別容器對過載的影響。考慮比較“myArrayList [0] == myString”。陣列列表元素是編譯時型別“物件”,因此使用引用等價。C#編譯器會警告你這個潛在錯誤,但是有許多類似的情況,編譯器不會對意外的引用相等發出警告。


3. 類陷阱

封裝你的資料

類負責正確地管理資料。出於效能原因,它們通常快取部分結果或者對其內部資料的一致性做出假設。資料公開訪問會影響你快取或做出假設的能力,對效能,安全性和併發性都有潛在影響。例如,暴露可變成員,如通用集合和陣列,允許使用者在你不知情的情況下修改這些結構。

屬性

屬性使你能夠精確控制使用者如何與你的物件進行互動,除了你透過訪問修改器控制的之外。具體來說,屬性使你能夠控制讀取和寫入時發生的情況。

屬性使你能夠建立穩定的API,同時重寫getter和setter中的資料訪問邏輯,或提供資料繫結源。

不要也不要讓屬性獲取器丟擲異常,避免修改物件狀態。這樣就意味著需要一種方法而不是屬性獲取器。

有關屬性的詳細資訊,請參閱MSDN的屬性設計主題: : 

仔細的使用getters,因為它有副作用。開發者認為成員訪問是一個微不足道的操作,所以他們經常忘記在程式碼審查期間考慮帶來的副作用。

物件初始化

你可以在建立表示式時,對新建立的物件設定屬性。使用特定值來建立Class Cde 物件,並用到Foo和Bar屬性:

new C {Foo=blah, Bar=blam}

你還可以使用特定的屬性名稱,建立匿名型別的例項:

var myAwesomeObject = new {Name=”Foo”, Size=10};

初始化會在構造主體執行之前執行,確保在進入構造器之前欄位已經初始化了。因為建構函式還沒有執行,所以欄位初始化器不能以任何方式引用“this”。

過度指定輸入引數

為了幫助防止特定方法的過度使用,請嘗試採用方法所需的最小特定型別。例如,考慮一個迭代List 的方法:

public void Foo(List bars) 
{ foreach(var b in bars)
  { // do something with the bar...  }
}

 對於其他的IEnumerable 集合,這段程式碼能夠很好地執行,但是透過為引數指定List ,你就需要集合必須是一個List。選擇引數的最小特定型別(IEnumerable ,ICollection 等),以確保方法的最大有用性


4. 泛型

泛型是一種十分有效的方式,來定義與型別無關的結構體和確保型別安全的演算法。

使用諸如List 之類的泛型集合,而不是無型別的集合如ArrayList,能夠提高型別的安全性和效能。

當實現泛型型別時,可以使用“default”關鍵字來獲取那種無法硬編碼到實現中的預設值。具體來說就是,數字型別的預設值為0; 引用和可空值型別的預設值為null。

T t = default(T);

 

5. 型別轉化

有兩種型別的conversions轉化。顯式轉換必須由開發人員呼叫,隱式轉換由編譯器基於上下文來應用。

Cast 描述
Tree tree =(Tree)obj; 如果obj是tree型別時,請使用這個。如果obj不是Tree型別,將產生一個InvalidCast異常。
Tree tree = obj as Tree; 當你無法確定obj是否是Tree型別時,請使用這個。如果obj不是Tree型別,將會給Tree分配一個空值。在必要時,請使用這種轉換方式,因為它需要對返回值進行條件處理。這些額外的程式碼可能產生更多的錯誤,使得程式碼更難以讀取和除錯。

型別轉化時,經常會遇到以下兩種情形:

  1. 表示式的執行時型別比編譯器能推斷出的型別更加具體。轉換指示編譯器將表示式當做更具體的型別來處理。如果你的假設不正確,編譯器將丟擲異常的程式碼。例如,從物件到字串的轉換。
  2. 轉換指示編譯器會生成關聯表示式的值的程式碼,如果沒有生成,則會丟擲異常。例如,從double到integer的轉換。

兩種型別轉換都是很危險的。第一種型別的轉換提出了一個問題,“為什麼開發人員知道,而編譯器不知道?”如果在這種情況下,嘗試更改程式,以便編譯器可以成功地推匯出正確的型別。如果你認為一個物件的執行時型別可能比編譯時型別更具體,那麼你可以使用“is”或“as”運算子。

第二種型別轉換引發了一個問題,“為什麼操作是在開始的地方執行的,而不是在目標資料型別?”如果你需要一個int型別的結果,使用int比double更有意義。

有關其他想法,請參閱:http//blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/

在顯式轉換是正確的操作情況下,透過使用適當的運算子來提高可讀性,除錯能力和可測試性。


6. 異常

異常不是條件

異常通常不應用於控制程式流; 它們代表的是,在執行時你可能無法恢復的意外情況。如果你預期你應該處理的情況,主動檢查情況,而不是等待異常發生。

要將格式不正確的字串正常轉換為數字,請使用TryParse()方法; 而不是丟擲異常,它返回一個布林值,指示解析是否成功。

使用異常處理範圍

在catch內部寫程式碼,並且仔細處理成程式塊。已執行過的程式碼已經不存在這些異常。例如:

Frobber originalFrobber = null; try {
  originalFrobber = this.GetCurrentFrobber(); this.UseTemporaryFrobber(); this.frobSomeBlobs();
} finally { this.ResetFrobber(originalFrobber);
}

如果GetCurrentFrobber()丟擲異常,那麼當finally block被執行時,originalFrobber仍然為null; 

明智的處理異常

只捕獲你準備處理的特定異常,並且只針對特定程式碼段。除非你的意圖只是簡單的記錄並重新丟擲異常。某些例外可能使應用程式處於一種狀態,那麼就需要避免處理所有異常或根類異常的例項。最好是在沒有進一步損壞的情況下應用已經崩潰,而不是試圖恢復並造成損害。你的恢復嘗試可能會在不經意間使事情更糟。

處理致命異常有一些細微差別,特別是關於finally block執行時,如何影響異常安全和偵錯程式。有關詳情,請參閱:http : //incrediblejourneysintotheknown.blogspot.com/2009/02/fatal-exceptions-and-why-vbnet-has.html

使用最高階異常處理,來安全到處理程式的意外情況並公開資訊以幫助除錯問題。請謹慎使用catch塊來解決本可以安全處理的特定情況,為無法預料的異常預留最高階的處理。

如果你捕獲到一個異常,那麼就需要採取一些措施來處理。不計其它後果地處理當前異常只會使問題難以識別和除錯。

對於公開了工作API的程式碼來說,將異常包含於自定義異常中,是特別有用的。異常是方法的可見介面的一部分,應該與引數和返回值一起被控制。可能導致更多異常的方法,是不應該被使用在可維護解決方案中的。

丟擲和重新丟擲異常

當你希望在更深層次處理一個捕獲到的異常時,維護原始異常狀態和堆疊對於除錯有極大的幫助。需要仔細地平衡,除錯和安全注意事項。

簡單的重新丟擲異常也是一個好選擇:

throw;

或者在新的throw中使用異常作為InnerException:

throw new CustomException(...,ex);

不要顯式地重新丟擲捕獲的異常,如下所示:

throw e;

這將復位異常狀態到當前行,並且阻止除錯。

一些異常發生在程式碼的上下文之外。對於這些情況,你可能需要新增事件的處理程式,如ThreadException或UnhandledException,而不是使用catch塊。例如,表單處理程式執行緒的上下文中引發的Windows窗體異常。

資料完整性

異常不得影響資料模型的完整性。你需要確保你的物件處於一致的狀態 - 不會違反類實現所做的任何假設。否則,透過“恢復”,你只能使你的程式碼變得混亂,之後還會導致進一步的損害。


7. 事件

事件和代理相互協助,當事件發生時,為類提供了一種方法來通知使用者。事件類似於委託型別的欄位; 當建立物件時,它們將自動初始化為null。

事件的值是一個多級代理。也就是一個可以依次呼叫其他代理的代理。你可以為事件分配委託; 可以透過+ =和 - =等運算子操作事件。

注意競逐條件

如果事件線上程之間共享,則有可能在你檢查null之後並且在呼叫它之前,另一個執行緒將刪除所有引數 – 就會丟擲NullReferenceException異常。

標準解決方案是建立事件的本地副本,用於測試和呼叫。你仍然需要小心,在其他執行緒中刪除的任何引數,在他們的委託被意外呼叫時會正常執行。你還可以實施鎖定,以一種能夠避免問題的方式為操作排佇列。

public event EventHandler SomethingHappened; private void OnSomethingHappened()
{ // The event is null until somebody hooks up to it
  // Create our own copy of the event to protect against another thread removing our subscribers
  EventHandler handler = SomethingHappened; if (handler != null)
    handler(this,new EventArgs());
}

更多關於時間和競逐的資訊,請參閱:http : //blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx

 

8. 屬性

屬性提供了一種方法,用於將元件,類和屬性的後設資料與其屬性的資訊一起輸入。它們通常用於向程式碼使用者提供資訊,如程式碼偵錯程式,測試框架和應用程式。你可以定義自己使用的屬性,也可以使用表中列出的預定義屬性。

屬性 使用 目的
Debugger顯示 偵錯程式 偵錯程式顯示格式
InternalsVisibleTo 會員訪問 能將內部成員暴露給特定的其他類。使用它,測試例程可以訪問受保護的成員。
預設值 屬性 指定屬性的預設值。

小心使用DebuggerStepThrough屬性,如果應用了這個屬性,會導致很難在方法中找到bug,因為你不能單步執行或打斷它們!


9. 除錯

除錯是任何開發工作中重要的組成部分。除了提供對執行時環境的常規不透明方面的可見性之外,偵錯程式可以進入執行時環境,同時偵錯程式還會導致應用程式的在沒有偵錯程式的情況下,獲的不同的結果。

使異常堆疊可見

要檢視當前框架的異常狀態,可以在Visual Studio Watch視窗中新增表示式“$ exception”。此變數包含當前異常狀態,類似於你在catch塊中看到的情況,除非你可以在偵錯程式中看到異常狀態,否則就不必在程式碼中實際捕獲異常。

注意訪問器中的副作用

如果你所使用的屬性有副作用,請考慮是否應使用屬性或偵錯程式設定,來防止偵錯程式自動呼叫getter。例如,你的類可能具有這些屬性:

private int remainingAccesses = 10; private string meteredData; public string MeteredData
{ get { if (remainingAccesses-- > 0) return meteredData; return null;
  }
}

第一次在偵錯程式中檢視此物件時,remainingAccesses將顯示為值10,MeteredData顯示為null。如果你將滑鼠懸停在remainingAccesses上,你會看到它的值現在是9。偵錯程式顯示的屬性值已經改變了物件的狀態。


10. 最佳化

早做計劃,經常衡量,然後最佳化

在設計期間設定合理的效能目標。在開發期間,專注於正確性而不是細微最佳化。經常根據目標衡量你的效果。如果你沒有達到目標,則應該花費時間來最佳化程式。

始終採用最合適的工具,在具有可重複性和儘可能接近使用者所經歷的實際條件的情況下,對效能進行經驗性測量。

由於CLR最佳化,有時效率低下的程式碼實際上比高效的程式碼執行速度更快。例如,CLR最佳化覆蓋了整個陣列的迴圈,以避免隱式的單元範圍檢查。開發人員通常在迴圈陣列之前計算長度:

int[] a_val = int[4000]; int len = a_val.Length; for (int i = 0; i < len; i++)
    a_val[i] = i;

透過將長度放在變數中,CLR可能無法識別模式,並將跳過最佳化。手動最佳化違反了直覺,會導致效能較差。

構建字串

如果你要做很多字串連線,應該使用System.Text.StringBuilder物件,這樣可以避免構建許多臨時字串物件。

對集合使用批處理操作

如果需要建立和填充已知大小的集合,請在建立集合時保留空間,以避免由於重複重新分配而導致的效能和資源問題。你可以使用AddRange方法(如List 中的方法)進一步提高效能:

Persons.AddRange(listBox.Items);


11. 資源管理

垃圾回收器能夠自動清理記憶體。即使如此,所有一次性資源,也必須妥善處理 - 特別是那些不由垃圾收集器管理的資源。

資源管理問題的常見來源
記憶體碎片 如果沒有足夠大的連續塊的虛擬地址空間,分配將失敗。
過程限制 程式通常訪問系統可用的記憶體和資源的嚴格子集。
資源洩漏 垃圾回收器只管理記憶體。其他資源需要由應用程式正確管理。
資源困境 依賴於垃圾收集器和終結器的資源在不再使用時,不會變得立即可用。事實上,它們可能永遠不可用。

使用try / finally塊來確保資源正確釋放,或讓你的類實現IDisposable,並利用更清潔和更安全的using語句。

using (StreamReader reader=new StreamReader(file)) 
{ //your code here

避免程式碼中使用垃圾收集器

儘量不要透過呼叫GC.Collect()干擾垃圾收集器,而應該將重點放在正確釋放或處置資源。當測量效能時,如果你能夠正確的評估影響,在小心的讓垃圾收集器執行。

避免編寫終結器

不同於最流行的錯誤認知,你的類不需要一個Finalizer,僅僅是因為它實現IDisposable!你可以實現IDisposable以使你的類能夠在任何所有的複合例項上呼叫Dispose,但是終結器只應在直接擁有非託管資源的類上實現。

Finalizer主要用於呼叫interop API,來處理Win32控制程式碼,SafeHandle更容易處理。

你不能推測你的終結器 - 它總是在終結器執行緒上執行 - 可以安全地與其他物件互動。那些其他物件本身可能已經完成了。


12. 併發

併發和多執行緒程式設計是一件很複雜和困難的事情。在嚮應用程式新增併發之前,請確保你真正瞭解自己正在做什麼 - 有很多細微之處需要了解!

多執行緒應用程式非常難以推理,並且容易受到諸如通常不影響單執行緒應用程式的競爭條件和死鎖等問題的影響。鑑於這些風險,你應該最後才考慮多執行緒。如果你必須使用多個執行緒,請儘量透過不線上程之間共享記憶體來最小化同步的需要。如果必須同步執行緒,請使用最高階別的同步機制。

最高階別,這些機制包括:

  • Async-await/Task Parallel Library/Lazy
  • Lock/monitor/AutoResetEvent
  • Interlocked/Semaphore
  • Volatile fields and explicit barriers

C#/ .NET中併發的複雜性很難就在這裡解釋清楚。如果你想要或需要開發一個利用併發的應用程式,請檢視詳細的文件,如O'Reilly的“Concurrency in C# Cookbook”。

使用volatile

將欄位標記為“易變”是高階功能,即使專家也經常誤解。C#編譯器將確保訪問欄位具有獲取和釋放語義; 這不同於確保對該欄位的所有訪問都處於鎖定狀態。如果你不知道什麼是獲取和釋放語義,以及它們如何影響CPU級最佳化,則應避免使用volatile欄位。相反,應該使用較高階別的工具,如任務並行庫或CancellationToken型別。

利用執行緒安全的內建方法

標準庫型別通常提供方便執行緒安全訪問物件的方法。例如,Dictionary.TryGetValue()。使用這些方法通常使你的程式碼更清潔,你不需要擔心如TOCTTOU or TOCTOU場景等資料競爭的情況。

不要鎖定“this”,字串或其他常見的公共物件

當實現在多執行緒上下文中使用的類時,要非常小心使用鎖。鎖定此字串或其他公共物件,會阻止封裝鎖定狀態,並可能導致死鎖。你需要防止其他程式碼鎖定你的實現上正在使用的物件; 最安全的做法是使用一個私人的物件成員。


13. 避免常見錯誤

引用null

不適當的使用null,是編碼缺陷的常見來源,可能會導致程式崩潰和其它意外行為。如果你嘗試訪問一個空引用,以為它是一個物件的有效引用一樣 - 例如,透過訪問一個屬性或方法,執行時將丟擲一NullReferenceException異常。

靜態和動態分析工具可以幫助你在釋出程式碼之前識別潛在的NullReferenceException異常。在C#中,空引用通常由尚未引用物件的變數引起。對於空值型別和引用型別來說,Null是一個有效值。例如,Nullable ,空委託,取消訂閱事件,會在“as”轉換,以及在許多其他情況下失敗。

每個空引用異常是都一個錯誤。不應該去捕獲NullReferenceException,而應該嘗試在使用它們之前測試物件是否為null。這也使得程式碼更容易被最小化try / catch塊讀取。

從資料庫表中讀取資料時,請確保,缺失值可以表示為DBNull物件,而不是空引用。不要指望它們表現的像潛在的空引用。

將十進位制值替換為二進位制數

浮點數和雙精度表示二進位制有理數,不是小數有理數,在儲存十進位制值時必須使用二進位制的近似值。從十進位制的角度來看,這些二進位制近似具有不一致的舍入和精度 - 有時導致算術運算的意外結果。因為浮點運算通常在硬體中執行,硬體條件可能會不可預測地加劇這些差異。

當小數精度非常重要時,使用十進位制,就像財務計算等情況。

修改結構

一個常見的錯誤情況是忘記結構體是值型別的,這就意味著它們被複制了並且透過值來進行傳遞。假設你有這樣的程式碼:

struct P { public int x; public int y; } void M()
{
   P p = whatever;
   …
   p.x = something;
   …
   N(p);

有一天,維護者決定將程式碼重構為:

void M()
{
   P p = whatever;
   Helper(p);
   N(p);
} void Helper(P p)
{ 
   …
   p.x = something;

現在當在M()中呼叫N(p)時,p具有錯誤的值。呼叫助手(p)傳遞p的副本,而不是p的引用,因此Helper()中執行的變化將丟失。相反,Helper會返回修改的p的副本。

意外的算術

C#編譯器保護你出現常量的算術溢位,但不一定是計算值。

忽略儲存返回值

與結構體不同,類是引用型別,方法可以修改引用的物件。然而,不是所有的物件方法都實際修改了引用的物件,一些會返回一個新物件。當開發人員呼叫後者時,他們需要記住將返回值賦給變數,以便使用修改後的物件。在程式碼審查期間,這種型別的問題通常在會被發現。一些物件,如字串,是不可變的,所以方法從不修改這些物件。即使如此,開發人員也會通常忘記。

例如,考慮string.Replace():

string label = “My name is Aloysius”;
label.Replace(“Aloysius”, “secret”);

 程式碼列印“我的名稱是Aloysius”,因為Replace方法不修改字串。

不要使迭代器/列舉器變得無效

不要在迭代時修改集合。

List myItems = new List{20,25,9,14,50}; foreach(int item in myItems)
{ if (item < 10)
    {
        myItems.Remove(item); // iterator is now invalid!
        // you’ll get an exception on the next iteration

如果你執行這個程式碼,一旦迴圈到集合中的下一個專案時。你會收到一個異常丟擲。

正確的解決方案,是使用第二個列表來儲存你要刪除的專案,然後在刪除時迭代該列表:

List myItems = new List{20,25,9,14,50};
List toRemove = new List(); foreach(int item in myItems)
{ if (item < 10)
   {
        toRemove.Add(item);         
   }
} foreach(int item in toRemove)
{

或者如果你使用C#3.0或更高版本,你可以使用List .RemoveAll。

就像這樣:

myInts.RemoveAll(item => (item < 10));

屬性名稱錯誤

在實現屬性時,請注意屬性名稱不同於類中使用的資料成員。在訪問屬性時,容易意外使用相同的名稱,並導致出現無限遞迴的情況。

// The following code will trigger infinite recursion
private string name; public string Name
{ get { return Name;  // should reference “name” instead.

當重新命名間接屬性時要小心。例如,WPF中的資料繫結,會將屬性名稱指定為字串。如果不小心更改該屬性名稱,你將會無意中建立了一個編譯器無法防護的問題。

以上就是所有C#開發人員應該知道的13件事情。
瞭解了C#開發中應該知道的13件事情,有助於我們更好地使用C#進行開發,當然在開發時,也可以藉助一些使用C#編寫的開發工具。如,這是一款專注於企業應用的.NET全功能控制元件套包,支援WinForms、WPF、UWP、ASP.NET MVC等多個平臺,幫助、在縮減成本的同時,提前交付豐富的桌面、Web和移動企業應用。

原文連結:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28298702/viewspace-2134085/,如需轉載,請註明出處,否則將追究法律責任。

相關文章