views:

93

answers:

4

So I've got these classes that expose a collection of child objects.

I don't want other classes adding or removing objects from collections because I need to wire into events in the child objects, so as they get added or removed I want to be able to do additional processing. But I really love the ease of manipulating generics internally.

Did I mention this is a WPF app so I need INotifySupport?

The best I can come up with is something like this.

public class foo : INotifyPropertyChanged
{
    protected List<ChildFoo> _Children = new List<ChildFoo>();

    public foo()
    {
    }

    public void AddChild(ChildFoo newChild)
    {
        DoAttachLogic(newChild);
        _Children.Add(newChild);
        NotifyPropertyChange("Children");
    }

    public void RemoveChild(ChildFoo oldChild)
    {
        DoRemoveLogic(oldChild);
        _Children.Remove(oldChild);
        NotifyPropertyChange("Children");
    }

    public ChildFoo[] Children
    {
        get
        {
            return _Children.ToArray();
        }
    }

}

Are there serious flaws with this design that I'm not seeing?

Every time the Children property is accessed we get the overhead of converting list to an array.

Any advice on this would be great.

+5  A: 

This is what I do for normal code:

Public Readonly Property Childern As ObjectModel.ReadOnlyCollection(Of Child)
    Get
       Return New ObjectModel.ReadOnlyCollection(Of Child)(_ChildernList)
    End Get
End Property

For WPF code I would just expose a subclass of ObservableCollection.

Jonathan Allen
-1 for the use of VB in response to C#
Tergiver
I do hope you aren't suggesting that C# developers aren't smart enough to read basic.
Jonathan Allen
seriously I can read VB just fine thank you.
Joel Barsotti
Questions tagged "C#" are almost invariably actually questions about the .NET framework and things built atop it. Downvoting useful answers for not using the language in the tag is not only foolish, it's harmful.
Robert Rossney
For the record, I didn't actually down-vote it, in fact I up-voted it. I may be able to read VB, but that doesn't mean I **want** to read it.
Tergiver
A: 

I changed the "add child" and "remove child" to protected since you are saying you don't want other classes modifying your collection. I changed your List to ObservableCollection so you can recieve collection changed notifications. Since you are using an IList there is no need to call ToArray(), just access directly.

try this:

public class foo : INotifyPropertyChanged
{
    protected ObservableCollection<ChildFoo> _Children = new ObservableCollection<ChildFoo>();

public foo()    {    }

protected void AddChild(ChildFoo oldChild)
{
    DoAttachLogic(newChild);
    _Children.Add(newChild);
    NotifyPropertyChange("Children");
}

protected void RemoveChild(ChildFoo oldChild)
{
    DoRemoveLogic(oldChild);
    _Children.Remove(oldChild);
    NotifyPropertyChange("Children");
}

public ChildFoo this[int n]
{
    get
    {
        return _Children[n];
    }
}

}
Muad'Dib
Well I want people to be able to increase or decrease the collection, but I need them to go through the class to do it, so I can run the addional logic on the objects that are being added/removed.
Joel Barsotti
so, basically what you want is a wrapper around a list. just change the protected add/remove to public in the code i gave, and you should be golden
Muad'Dib
A: 

You could subclass BindingList and set AllowNew/AllowRemove to false. In your Child Add/Remove methods, you can set it to true, make the changes, then set it back to false. (Of course, you need to hide set access to AllowNew/AllowRemove from outside callers as well).

Another option - subclass Observable collection and override the InsertItem, RemoveItem, etc methods to behave as AddChild/RemoveChild would behave. Then callers can still access it in familiar ways, but not bypass your custom logic.

Subclassing an existing collection class is probably going to be easier (for you and the consumer) than wrapping a collection in another class.

Turntwo
A: 

You should use ObservableCollection as field in your class, you then have full access to modify collection. Then expose this as ReadonlyObservableCollection via property. And if you dont change collection itself (eg. nochildren = new ObservableCollection(), you should make field readonly), then you dont need any kind of notifyPropertyChanged on this property, because it doesnt change and collection itself handles those events for its children.

public class Child
{
    public int Value { get; set; }
}

class MyClassWithReadonlyCollection
{
    private readonly ObservableCollection<Child> _children = new ObservableCollection<Child>();

    public MyClassWithReadonlyCollection()
    {
        _children.Add(new Child());
    }

    //No need to NotifyPropertyChange, because property doesnt change and collection handles this internaly
    public ReadOnlyObservableCollection<Child> Children { get { return new ReadOnlyObservableCollection<Child>(_children); } }
}
Euphoric