2016-04-23 6 views
4

Voici cet exemple de code:C# HashSet <T> lecture seule solution

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(); 
     } 
    } 
} 

Et voici un code de test:

// 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"); 

Mes questions sont les suivantes:

est-il une meilleure solution pour la propriété "Real_ReadonlyHashSet"?

Microsoft va-t-il un jour implémenter la méthode "AsReadOnly" dans le HashSet? <T>?

+0

Il existe un [ImmutableHashSet] (https://msdn.microsoft.com/en-us/library/dn467171 (v = vs.111) .aspx) –

+0

Il n'est pas si difficile de l'écrire vous-même: 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 –

Répondre

6

Voici the entirety of the code de .AsReadOnly()

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

La première ligne est même pas nécessaire si vous ne l'utilisez CodeContracts. Toutefois, ReadOnlyCollection<T> prend uniquement en charge IList<T> que HashSet<T> ne prend pas en charge.

Ce que je ferais est de faire votre propre classe ReadOnlySet<T> qui prend un ISet<T> et ne passe que par les opérations de lecture like ReadOnlyCollection<T> does internally.

MISE À JOUR: Voici une étoffée pleinement ReadOnlySet<T> je me suis vite écrit le long d'une méthode d'extension qui ajoute une .AsReadOnly() à tout ce qui met en œuvre 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

Vous pouvez écrire votre propre implémentation d'un IReadOnlyCollection<T> qui enveloppe un IEnumerable<T> et un nombre:

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; } 
} 

Ensuite, vous déclarez votre propriété ReadonlyHashSet comme ceci:

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

Je pense que cela résoudrait le problème.

+0

Je pense qu'il vaudrait mieux passer ICollection de cette façon que vous pouvez transmettre '.Contains (' qui est la partie la plus puissante d'un HashSet –

+0

@ScottChamberlain Je gardais le type de retour le même que dans l'OP - bien sûr, 'Contains()' n'est pas un membre de 'IReadOnlyCollection '. Je suppose que l'OP voulait indiquer que la valeur était en lecture seule via le type plutôt que via une propriété ('IsReadOnly') –

+0

Ah, je passais par' ReadOnlyCollection 'qui [passe le long] (https: // msdn.microsoft.com/en-us/library/ms132478(v=vs.110).aspx), pas 'IReadOnlyCollection ' –