2016-04-28 79 views
4

我有一個私人的HashSet<string>這是一個只讀屬性的支持字段,它應該返回一個只讀集合,使調用者不能修改集合。於是,我就:如何在不復制元素的情況下從HashSet創建ReadOnlyCollection?

public class MyClass 
{ 
    private readonly HashSet<string> _referencedColumns; 

    public ICollection<string> ReferencedColumns { 
     get { return new ReadOnlyCollection<string>(_referencedColumns); } 
    } 

這並不編譯爲ReadOnlyCollection接受IList<T>其不受HashSet<T> implememted。有沒有另外的包裝我可以用來拯救我從複製項目?出於我的目的,只需返回執行ICollection<T>(而不是IList<T>)的內容就足夠了,這是由HashSet<T>執行的。

+0

如果它是重要的泰德塔主叫方無法修改回報,看看[Immutability和ReadOnlyCollection ](https://blogs.msdn.microsoft.com/jaredpar/2008/04/22/immutability-and-readonlycollectiont/),也許[這個問題(http ://stackoverflow.com/questions/285323/best-practice-how-to-expose-a-read-only-icollection) – stuartd

回答

7

請考慮將該屬性暴露爲類型IReadOnlyCollection<>而不是,它將提供HashSet<>的只讀視圖。這是一種有效的實現方式,因爲屬性獲取器不需要底層集合的副本。

這不會阻止某人將物業轉到HashSet<>並對其進行修改。如果您擔心這一點,請在屬性獲取器中考慮return _referencedColumns.ToList(),該屬性獲取器將創建基礎集合的副本。

+0

謝謝。那麼是否沒有包裝可以節省複製開銷並且不能被回收? – Dejan

+6

只用於記錄:投射到'IReadOnlyCollection <>'只適用於.NET 4.6及更高版本(請參閱:http://stackoverflow.com/a/32762752/331281)。 – Dejan

+0

好的電話。這仍然會使用HashSet的索引行爲,儘管這不再明顯。調用IReadOnlyCollection.Contains()'強烈的線性時間實現,即使情況並非如此。 – Timo

4

您可以使用下面的裝飾包裹的哈希集合,並返回是隻讀的ICollection<T>(在IsReadOnly屬性返回true,如在ICollection<T>合同中規定的改性方法拋出NotSupportedException):

public class MyReadOnlyCollection<T> : ICollection<T> 
{ 
    private readonly ICollection<T> decoratedCollection; 

    public MyReadOnlyCollection(ICollection<T> decorated_collection) 
    { 
     decoratedCollection = decorated_collection; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return decoratedCollection.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return ((IEnumerable) decoratedCollection).GetEnumerator(); 
    } 

    public void Add(T item) 
    { 
     throw new NotSupportedException(); 
    } 

    public void Clear() 
    { 
     throw new NotSupportedException(); 
    } 

    public bool Contains(T item) 
    { 
     return decoratedCollection.Contains(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     decoratedCollection.CopyTo(array, arrayIndex); 
    } 

    public bool Remove(T item) 
    { 
     throw new NotSupportedException(); 
    } 

    public int Count 
    { 
     get { return decoratedCollection.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return true; } 
    } 
} 

你可以使用它像這樣:

public class MyClass 
{ 
    private readonly HashSet<string> _referencedColumns; 

    public ICollection<string> ReferencedColumns { 
     get { return new MyReadOnlyCollection<string>(_referencedColumns); } 
    } 
    //... 

請注意,此解決方案將不採取HashSet中的快照,而是將持有的HashSet的一個參考。這意味着返回的集合將包含HashSet的活動版本,即如果HashSet發生更改,在更改之前獲取只讀集合的​​消費者將能夠看到更改。

+0

謝謝!當然,我可以寫我自己的。我想知道BCL是否有口袋裏有東西。順便說一句,如果你的'MyReadOnlyCollection'實現'IReadOnlyCollection <>'會更好嗎? – Dejan

+0

不客氣。這取決於消費者。它更喜歡'IReadOnlyCollection '? –

+0

'IReadOnlyCollection '就足夠了,它完美記錄了呼叫者的期望。我應該首先在我的問題中使用它,但現在我不會爲了保留歷史而改變它。 – Dejan

2

雖然它不是隻讀,微軟發佈了一個NuGet包叫System.Collections.Immutable包含一個ImmutableHashSet<T>它實現IImmutableSet<T>延伸IReadOnlyCollection<T>

快速用法示例:

public class TrackedPropertiesBuilder : ITrackedPropertiesBuilder 
{ 
    private ImmutableHashSet<string>.Builder trackedPropertiesBuilder; 

    public TrackedPropertiesBuilder() 
    { 
     this.trackedPropertiesBuilder = ImmutableHashSet.CreateBuilder<string>(); 
    } 

    public ITrackedPropertiesBuilder Add(string propertyName) 
    { 
     this.trackedPropertiesBuilder.Add(propertyName); 
     return this; 
    } 

    public IImmutableSet<string> Build() 
     => this.trackedPropertiesBuilder.ToImmutable(); 
} 
相關問題