views:

90

answers:

3

I have this weird situation.

I have these two classes:

Public Class Entry

End Class

Public Class Core

End Class

One of the properties of the Core class will be an array of Entry objects. How can I declare it?

Now, the only way to change (add/remove) this array from outside should be using two functions - AddEntry(Ent As Entry) and RemoveEntry(ID As String). Note that here, whoever is calling the AddEntry function should only be bothered with creating an Entry object and passing it. It will be added to the existing array.

But, the Entry array should be accessible like this from outside, for looping through and printing or whatever like this:

' core1 is a valid Core object
For Each Ent As Entry In core1.Entries
  MsgBox(Ent.SomeProperty)
Next Ent

Is it possible to expose the Array as a property but restrict modification through functions alone? I know that the logic inside the Add and Remove functions can be inside the setter or getter, but the person wanting to add should pass only a single Entry object.

It is like saying You have readonly access to the array, but for modifying it, just create an object and send it or the ID to remove it. Don't bother about the entire array.

I hope I am making sense.

A: 

Create a private field for the array and then create your accessing methods to work with the array internally. In order to expose this array to callers so that they can enumerate it you should expose a property of type IEnumerable(Of T).

This approach is not foolproof, however as a clever caller could simply cast the IEnumerable(Of T) back to an array and modify it so it may be necessary to create a copy of the array and return that as the IEnumerable(Of T). All this has obvious performance penalties as I am sure you already see. This is just one of many issues that can arise when arrays are used as underlying data structures.

Andrew Hare
Oh. Can you post some more code on how to use it? or a sample usage in some article? That would be helpful.
Senthil
+1  A: 

Why do you want to expose it as an array ?

What I would do, is use a List internally to store the entries. (That List would be private) Create the necessary public methods (AddEntry / RemoveEntry / ... ), which manipulate the private list.

Then, create a public property which exposes the List, but in a ReadOnly fashion. That is, that property should return an ReadOnlyCollection instance. Like this:

(I know it is in C#, but that 's my 'main language' - a bit too lazy to convert it to VB.NET)

public class Core
{
    private List<Entry> _entries = new List<Entry>();

    public void AddEntry( Entry entry )
    {
        _entries.Add (entry);
    }

    public ReadOnlyCollection<Entry> Entries
    {
       get { return _entries.AsReadOnly(); }
    }
}
Frederik Gheysels
+1 Hey no prob, I'll figure out how to convert to VB :) Thanks for the code. I'll see if I am able to get it working.
Senthil
Can a normal array be returned as ReadOnly? Dim entries() as Entry. Can entries be returned as ReadOnly? Or should I do it only on List, ArrayList or such objects?
Senthil
But, is there a reason you really want to use an array internally ?I think it will make things only more complicated.Perhaps you can create a ReadOnlyCollection instance by passing the array in its constructor.if not, then you can create a List by passing the array to the constructor of the List, and then calling 'AsReadOnly' on the list, to get a ReadOnlyCollection instance.
Frederik Gheysels
There is no real reason. Just curiosity :D So it ain't possible?
Senthil
Also, If I have a list and return like you have in a getter method, will I be able to loop through using a ForEach?
Senthil
Yes, you will be able to loop over the ReadOnlyCollection using a foreach, since ReadOnlyCollection implements IEnumerable. :)
Frederik Gheysels
It is possible to use an array internally, since you can do this:int[] arr = new int[] { 1, 2, 3, 4, 5 }; ReadOnlyCollection<int> coll = new ReadOnlyCollection<int> (arr);
Frederik Gheysels
Massing class keyword? `public class Core`
Mark Byers
Thanks Frederik :)
Senthil
A: 

You can keep the List private and instead return an IEnumerable. Code generated via Reflector - I hope it's readable:

Public Class Core
    Public Sub AddEntry(ByVal Ent As Entry)
        Me.entries.Add(Ent)
    End Sub

    Public Sub RemoveEntry(ByVal ID As String)
        Dim pred As Predicate(Of Entry) = Function (ByVal entry As Entry) 
            Return (entry.Id = ID)
        End Function
        Me.entries.RemoveAll(pred)
    End Sub

    Public ReadOnly Property Entries As IEnumerable(Of Entry)
        Get
            Return Me.entries
        End Get
    End Property

    Private entries As List(Of Entry) = New List(Of Entry)
End Class

Note: I'd recommend using a List<Entry> instead of an array if you'll be adding and removing objects - or perhaps even a Dictionary<string, Entry> given the way you are using it.

Mark Byers
Some smart-ass can cast the IEnumerable property to a List, and then, he can add / remove objects from that list, without using the AddEntry / RemoveEntry methods. :)
Frederik Gheysels
Sure. It is unrealistic to prevent malicious users from modifying the underlying collection unless you do a lot of extra work.
Mark Byers
What about the ReadOnly thing suggested in the other answer? Is there a possibility of a malicious user somehow going around that too?
Senthil
Yes, you can always get around it using reflection if the only protection is to make things private.
Mark Byers
You should choose between the two depending on whether you want to allow people only to iterate over the elements as in your example, or also to index into the array.
Mark Byers