2016-04-23 22 views
4

Hier ist das Beispielcode:C# HashSet <T> read-only Abhilfe

static class Store 
{ 
    private static List<String> strList = new List<string>(); 
    private static HashSet<String> strHashSet = new HashSet<string>(); 

    public static List<String> NormalList 
    { 
     get { return strList; } 
    } 

    public static HashSet<String> NormalHashSet 
    { 
     get { return strHashSet; } 
    } 

    public static IReadOnlyList<String> ReadonlyList 
    { 
     get { return (IReadOnlyList<String>)strList; } 
    } 

    public static IReadOnlyCollection<String> ReadonlyHashSet 
    { 
     get { return (IReadOnlyCollection<String>)strHashSet; } 
    } 

    public static IReadOnlyList<String> Real_ReadonlyList 
    { 
     get { return (IReadOnlyList<String>)strList.AsReadOnly(); } 
    } 

    public static IReadOnlyCollection<String> Real_ReadonlyHashSet 
    { 
     get 
     { 
      List<String> tmpList = new List<String>(strHashSet); 
      return (IReadOnlyList<String>)(tmpList).AsReadOnly(); 
     } 
    } 
} 

Und hier ist ein Testcode:

// normal behaviour 
// you can modify the list and the hashset 

Store.NormalList.Add("some string 1"); 

Store.NormalHashSet.Add("some string 1"); 

// tricky behaviour 
// you can still modify the list and the hashset 

((List<String>)Store.ReadonlyList).Add("some string 2"); 

((HashSet<String>)Store.ReadonlyHashSet).Add("some string 2"); 

// expected read-only behaviour 
// you can NOT modify 

// throws InvalidCastException 
((List<String>)Store.Real_ReadonlyList).Add("some string 3"); 
// throws InvalidCastException 
((HashSet<String>)Store.Real_ReadonlyHashSet).Add("some string 3"); 

Meine Fragen sind diese:

Gibt es eine bessere Lösung für die Eigenschaft "Real_ReadonlyHashSet"?
Wird Microsoft eines Tages die "AsReadOnly" -Methode zum HashSet <T>?

+0

Es gibt ein [ImmutableHashSet] (https://msdn.microsoft.com/en-us/library/dn467171 (v = vs.111) .aspx) –

+0

Es ist auch nicht so schwer, es nur selbst zu schreiben: https: //github.com/airbreather/AirBreather.Common/blob/aba09330ae3066cb46ad7e0ee963e00d27e63cb6/Source/AirBreather.Common/AirBreather.Common/Collections/ReadOnlySet.cs https://github.com/airbreather/AirBreather.Common/blob/aba09330ae3066cb46ad7e0ee963e00d27e63cb6/Source /AirBreather.Common/AirBreather.Common/Utilities/EnumerableUtility.cs#L47 –

Antwort

6

ist hier the entirety of the code von .AsReadOnly()

public ReadOnlyCollection<T> AsReadOnly() { 
    Contract.Ensures(Contract.Result<ReadOnlyCollection<T>>() != null); 
    return new ReadOnlyCollection<T>(this); 
} 

Die erste Zeile nicht einmal notwendig, wenn Sie nicht CodeContracts verwenden. ReadOnlyCollection<T> unterstützt jedoch nur IList<T>, die HashSet<T> nicht unterstützt.

Was ich tun würde, ist Ihre eigene ReadOnlySet<T> Klasse, die in einem ISet<T> nimmt und nur die Lesevorgänge like ReadOnlyCollection<T> does internally durchläuft.

UPDATE: Hier ist ein vollständig aus prall ist ReadOnlySet<T> ich schnell zusammen mit einer Erweiterungsmethode schrieb up, die eine .AsReadOnly() auf etwas hinzufügt, die ISet<T>

public static class SetExtensionMethods 
{ 
    public static ReadOnlySet<T> AsReadOnly<T>(this ISet<T> set) 
    { 
     return new ReadOnlySet<T>(set); 
    } 
} 

public class ReadOnlySet<T> : IReadOnlyCollection<T>, ISet<T> 
{ 
    private readonly ISet<T> _set; 
    public ReadOnlySet(ISet<T> set) 
    { 
     _set = set; 
    } 

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

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

    void ICollection<T>.Add(T item) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public void UnionWith(IEnumerable<T> other) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public void IntersectWith(IEnumerable<T> other) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public void ExceptWith(IEnumerable<T> other) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public void SymmetricExceptWith(IEnumerable<T> other) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public bool IsSubsetOf(IEnumerable<T> other) 
    { 
     return _set.IsSubsetOf(other); 
    } 

    public bool IsSupersetOf(IEnumerable<T> other) 
    { 
     return _set.IsSupersetOf(other); 
    } 

    public bool IsProperSupersetOf(IEnumerable<T> other) 
    { 
     return _set.IsProperSupersetOf(other); 
    } 

    public bool IsProperSubsetOf(IEnumerable<T> other) 
    { 
     return _set.IsProperSubsetOf(other); 
    } 

    public bool Overlaps(IEnumerable<T> other) 
    { 
     return _set.Overlaps(other); 
    } 

    public bool SetEquals(IEnumerable<T> other) 
    { 
     return _set.SetEquals(other); 
    } 

    public bool Add(T item) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

    public void Clear() 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

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

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

    public bool Remove(T item) 
    { 
     throw new NotSupportedException("Set is a read only set."); 
    } 

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

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

implementiert Sie könnten Ihre eigene Implementierung eines schreiben IReadOnlyCollection<T>, die eine IEnumerable<T> und eine Zählung hüllt:

public sealed class ReadOnlyCollectionFromEnumerable<T>: IReadOnlyCollection<T> 
{ 
    readonly IEnumerable<T> _data; 

    public ReadOnlyCollectionFromEnumerable(IEnumerable<T> data, int count) 
    { 
     _data = data; 
     Count = count; 
    } 

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

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

    public int Count { get; } 
} 

Dann erklären Sie ReadonlyHashSet Eigenschaft wie folgt:

public static IReadOnlyCollection<String> ReadonlyHashSet 
{ 
    get { return new ReadOnlyCollectionFromEnumerable<string>(strHashSet, strHashSet.Count); } 
} 

Ich denke, dass das Problem lösen würde.

+0

Ich denke, es wäre besser, ICollection 'auf diese Weise zu übergeben, die Sie' .Contains übergeben können ('das ist der mächtigste Teil eines HashSet –

+0

@ScottChamberlain Ich behielt den Rückgabetyp wie im OP - natürlich ist 'Contains()' kein Mitglied von 'IReadOnlyCollection '. Ich nehme an, der OP wollte angeben, dass der Wert über den Typ schreibgeschützt war und nicht über eine Eigenschaft ('IsReadOnly'). –

+0

Ah, ich ging mit' ReadOnlyCollection 'welche [gibt es weiter] (https: // msdn.microsoft.com/en-us/library/ms132478(v=vs.110).aspx), nicht 'IReadOnlyCollection ' –