DGH
DGH

Reputation: 11549

(C#) iterate over read-only private collection member

I have a class which has two HashSet<String> collections as private members. Other classes in my code would like to be able to iterate over those HashSets and read their contents. I don't want to write a standard getter because another class could still do something like myClass.getHashSet().Clear(); Is there any other way to expose the elements of my HashSets to iteration without exposing the reference to the HashSet itself? I'd love to be able to do this in a way that is compatible with for-each loops.

Upvotes: 2

Views: 1634

Answers (7)

Alrekr
Alrekr

Reputation: 309

This might be quite a bit too late to the party but the easiest way today would be to use Linq. Instead of writing

public IEnumerable<string> GetValues() 
{
    foreach(var elem in list)
        yield return elem; 
}

you can write

public IEnumerable<string> GetValues() => list;

Upvotes: 1

Jeffrey L Whitledge
Jeffrey L Whitledge

Reputation: 59503

You may also provide a sequence like this:

public IEnumerable<string> GetHashSetOneValues()
{
    foreach (string value in hashSetOne)
        yield return value;
}

This method may then be called within a foreach loop:

foreach (string value in myObject.GetHashSetOneValues())
    DoSomething(value);

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1502226

Assuming you're using .NET 3.5, one alternative to writing the yield code yourself is to call a LINQ method. For example:

public IEnumerable<string> HashSet
{
    get { return privateMember.Select(x => x); }
}

or

public IEnumerable<string> HashSet
{
    get { return privateMember.Skip(0); }
}

There are various LINQ operators which could be used like this - using Skip(0) is probably the most efficient, as after the initial "skip 0 values" loop, it's probably just the foreach/yield return loop shown in the other answers. The Select version will call the no-op projection delegate for each item yielded. The chances of this difference being significant are astronomically small, however - I suggest you go with whatever makes the code clearest to you.

Upvotes: 6

Bryan Watts
Bryan Watts

Reputation: 45465

You can also use the Select method to create a wrapper than can't be cast back to HashSet<T>:

public IEnumerable<int> Values
{
    get { return _values.Select(value => value);
}

This avoids iterating over _values twice, as you would with .ToArray(), but keeps the implementation to a single clean line.

Upvotes: 3

strager
strager

Reputation: 90042

Expose a IEnumerable<T> property:

public IEnumerable<whatevertype> MyHashSet {
    get {
        return this.myHashSet;
    }
}

Of course, the user of this code can cast that IEnumerable<T> to a HashSet<T> and edit elements, so to be on the safe side (while hurting performance), you can do:

public IEnumerable<whatevertype> MyHashSet {
    get {
        return this.myHashSet.ToArray();
    }
}

or:

public IEnumerable<whatevertype> MyHashSet {
    get {
        foreach(var item in this.myHashSet) {
            yield return item;
        }
    }
}

A more performant method of protection, but less convenient to the caller, is to return an IEnumerator<T>:

public IEnumerator<whatevertype> GetMyHashSetEnumerator() {
    return this.myHashSet.GetEnumerator();
}

Upvotes: 3

Michael Meadows
Michael Meadows

Reputation: 28426

Make your getter expose the HashSet as IEnumerable.

private HashSet<string> _mine;

public IEnumerable<string> Yours
{
    get { return _mine; }
}

If the generic type is mutable, then that can still be modified, but no items can be added or removed from your HashSet.

Upvotes: -1

Morten Mertner
Morten Mertner

Reputation: 9474

Add a method/property like this to avoid exposing the actual container:

public IEnumerable EnumerateFirst()
{
     foreach( var item in hashSet )
         yield return item;
}

Upvotes: 3

Related Questions