2010-04-04 62 views
4

拋出以下實例的正確異常是什麼?正確使用.NET異常

如果,例如,我有一個類:專輯與歌曲的集合:

List<Song> 

而且專輯內的方法來添加一個

public void AddSong(Song song) 
{ 
    songs.Add(song); 
} 

如果用戶嘗試添加已存在的歌曲,我應該拋出異常嗎?如果是這樣,哪種類型的異常?

我聽說過「只在特殊情況下使用異常」這個短語,但我想告訴客戶端實現Album的確切內容出了什麼問題(不只是返回一個布爾值)。

回答

4

如果您的用例暗示集合中的項目應該是唯一的,那麼您應該使用強制執行該項目的數據結構。

通過這樣做,您不僅可以避免必須編寫一個O(N)查找方法來檢查重複項,而且還可以調出預先存在的重複鍵異常,以便將此類集合拋出。

但是,.NET沒有保留排序順序的獨特集合,儘管很容易擴展List來支持排序順序。

下面我使用的方法通過在第二個HashSet中存儲唯一值來犧牲速度的內存佔用空間。如果內存大小更重要,則只需對每個添加操作執行O(N)檢查。由於List中的方法不是虛擬的(出於某種原因),我導致使用new關鍵字隱藏基本方法。

請注意,這只是一個示例,並不是線程安全的,應該不會用於真正的生產應用程序。

public class UniqueList<T> : List<T> 
    { 
     private HashSet<T> _internalHash = new HashSet<T>(); 

     public UniqueList() : base() { } 
     public UniqueList(IEnumerable<T> collection) : base(collection) { } 
     public UniqueList(int capacity) : base(capacity) { } 

     public new void Add(T item) 
     { 
      if (!_internalHash.Add(item)) 
       throw new ArgumentException("Item already exists in UniqueList"); 

      base.Add(item); 
     } 

     public new void AddRange(IEnumerable<T> collection) 
     { 
      foreach (T t in collection) 
      { 
       this.Add(t); 
      } 
     } 

     public new bool Remove(T item) 
     { 
      _internalHash.Remove(item); 
      return base.Remove(item);    
     } 

     public new int RemoveAll(Predicate<T> match) 
     { 
      int removedElems = 0; 

      foreach (T item in this) 
      { 
       if (match(item)) 
       { 
        this.Remove(item); 
        removedElems++; 
       } 
      } 

      return removedElems; 
     } 

     public new void RemoveAt(int index) 
     {     
      this.Remove(this[index]);    
     } 

     public new void RemoveRange(int index, int count) 
     { 
      for (int i = index; i < count; i++) 
      { 
       this.Remove(this[i]); 
      } 
     } 
    } 
+0

他們可能應該使用'HashSet',然後如果'Add'返回false,他們可以拋出異常。 – ChaosPandion 2010-04-04 06:45:58

+1

@Chaos:儘管如此,一組並沒有排序。 – Joey 2010-04-04 06:49:09

+1

@Johannes - 是的,在MSDN上驗證。我想我以前從來不需要關心這個事實。 – ChaosPandion 2010-04-04 06:52:43

0

您可以隨時創建自己的例外。只需創建一個繼承自Exception的類(或者,在本例中爲ArgumentException)。

沿着DuplicateItemException(或DuplicateSongException如果你想要的東西非常具體)的東西聽起來是正確的。

3

而不是拋出異常,你可以有AddSong方法返回一個布爾 - 如果歌曲添加成功,否則爲假真。就我個人而言,如果期望歌曲在收藏中獨一無二,我認爲在這種情況下拋出異常是可以接受的。例如,如果收藏品是專輯中的歌曲列表,則不合理地期望重複歌曲(相同標題,相同持續時間,軌道序列中的相同位置等)。你可以選擇創建你自己的異常類派生自System.Exception來創建自定義錯誤,如果你想,所以你可以拋出一個異常,解釋了錯誤發生的原因。

+5

.NET Framework有一個特定的命名方案,其中方法返回「bool」成功值而不是拋出異常。這些方法的名字以'Try'開始;例如'TryParse'與'Parse'或'Dictionary <>。TryGetValue'。你也可以提供兩種方法,一種拋出異常,一種返回成功值。 – stakx 2010-04-04 07:26:22

0

如果你想提供有用的例外,你可能希望有一個基本的例外。

AlbumException

然後從CMerat的答案創建服用。

DuplicateSongException

這當然應該繼承AlbumException

就我個人而言,我會讓Album類不可變。在這種情況下,整個情況就會消失。