2010-05-03 67 views
1

我在寫一個接口,它有一個我只想讀取的集合屬性。我不希望界面的用戶能夠修改集合。我發現創建一個只讀集合屬性的典型建議是屬性的類型設置爲IEnumerable的是這樣的:正在使用YIELD以只讀方式返回集合?

private List<string> _mylist; 
public IEnumerable<string> MyList 
{ 
get 
    { 
     return this._mylist; 
    } 
} 

然而,這並不妨礙用戶從鑄造了IEnumerable回到列表和修改它。

如果我使用Yield關鍵字而不是直接返回_mylist,這將阻止我的界面的用戶能夠修改集合。我認爲是這樣,因爲我只是一個接一個地返回對象,而不是實際的集合。

private List<string> _mylist; 
public IEnumerable<string> MyList 
{ 
get 
    { 
     foreach(string str in this._mylist) 
     { 
      yield return str; 
     } 
    } 
} 

回答

0

是的,結果序列將是隻讀的。它還具有延遲執行的優點,因此只有在某些客戶端代碼枚舉了序列中的項目之後纔會實際評估循環(儘管如果在客戶端代碼試圖枚舉內部集合之前更改內部集合,這可能會產生意想不到的後果) 。

+1

在這裏,延期執行只不過是開銷。 – SLaks 2010-05-03 23:27:00

+1

有時我*強制*評估只是爲了避免延誤的副作用(ick !!!) – 2010-05-03 23:28:59

4

是的,它是不可變的。

但是,您應該改用ReadOnlyCollection<string>

例如:

MyClassName() { //(Constructor) 
    MyList = new ReadOnlyCollection<string>(_myList); 
} 

private List<string> _mylist; 
public ReadOnlyCollection<string> MyList { get; private set; } 
+0

是的,但請注意,「不變性」的一部分來自包含的對象,在這種情況下'字符串'也是不可變的。一般來說,調用者不能更改集合的成員資格,但可以更改內容(對於ref類型)。有時候這是可取的,有時候不是。 – 2010-05-03 23:32:18

+1

@Ben:對於ref類型的任何集合都是如此,除非你克隆每一個元素,否則一般包括IEnumerable ... – 2010-05-03 23:33:38

+0

我的情況集合包含不可變對象(一個帶有私有集合訪問器的結構),所以這不是問題。 – 2010-05-03 23:46:04

3

沒錯,就是工作。這不是唯一的方法。下面是工作在.NET 3.5的另一種方式:

public IEnumerable<string> MyList 
{ 
    get { return _myList.Skip(0); } 
} 

從這個question這也顯示了一些其他的方法可以做到這兩者。

(使用Google找到)。

5

這是一種合理的方式來阻止您的班級的用戶回到列表並訪問成員。另一種選擇是使用_mylist。 AsReadOnly(),它返回ReadOnlyCollection<T>

但是,我建議不要擔心用戶如何通過投射到其他內部類型來「破壞」您的API。如果您將公共API設置爲IEnumerable<T>,則這是最終用戶將(或應該)使用的內容 - 而不是嘗試投射到List<T>。試圖阻止用戶做一些不恰當的事情,並且像這樣打破你的API是有問題的,特別是因爲你無法防止所有不合適的用法。

+0

使用部分信任沙箱(在.NET或Java中)時,您的最後一句不再是真的。 – 2010-05-03 23:30:37

+0

@本:真的。但總的來說,我發現人們在沒有理由這樣做的情況下試圖阻止投射。但是,在部分信任沙箱中,您可能不會直接提供訪問權限,無論如何您都不必擔心這一點。 – 2010-05-03 23:34:33

1

如果使用你的類的代碼具有完全的信任,它總是可以反射和訪問私人領域。這將是相當不禮貌的,但鑄造返回值只會稍好一些。如果用戶投擲你的財產並腐敗國家,這是他們自己的錯。

直接返回列表實例作爲IEnumerable的一個好處是,LINQ可以優化像ElementAt()或Count()這樣的方法的訪問,而不會違反IEnumerable的基本約定(因爲兩者都可以通過枚舉來完成)。