Erik Lieben
Erik Lieben

Reputation: 1247

C# Object that can be used as IEnumerable and Single object

Is there any better/nicer way to do the following?

Sample:

static void Main(string[] args)
{
    Item i = new Item() {Name="Erik"};
    i.Add(new Item() {Name="Fred"});
    i.Add(new Item() {Name="Bert"});

    // First access the object as collection and foreach it
    Console.WriteLine("=> Collection of Items");
    foreach (Item item in i)
        Console.WriteLine(item.Name);

    // Access a single property on the object that gives back the first element in the list.
    Console.WriteLine("=> Single Item");
    Console.WriteLine(i.Name);

    Console.ReadLine();
}


public class Item : IEnumerable<Item>
{
    public void Add(Item item)
    {
        _items.Add(item);
        UpdateInnerList();
    }

    #region Fields

    private List<Item> _items = new List<Item>();
    private List<Item> _innerList = new List<Item>();           
    private string _name = string.Empty;

    #endregion

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            UpdateInnerList();
        }
    }

    #region Methods

    public IEnumerator<Item> GetEnumerator()
    {
        return _innerList.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _innerList.GetEnumerator();
    }

    private void UpdateInnerList()
    {
        _innerList.Clear();
        _innerList.Add(this);
        _innerList.AddRange(_items);
    }

    #endregion
}

Upvotes: 2

Views: 701

Answers (2)

Michal B.
Michal B.

Reputation: 5719

Did you mean something like this?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleTestCSharp
{
    class Node : IEnumerable<Node>
    {
        public string Name { get; private set; }
        private List<Node> _list = new List<Node>();

        public Node(string name)
        {
            Name = name;
        }

        public void Add(Node child)
        {
            _list.Add(child);
        }

        public Node this[string name]
        {
            get
            {
                return _list.First(el => el.Name == name);
            }
        }

        public IEnumerator<Node> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        public override string ToString()
        {
            return Name;
        }
    }   

    class Program
    {

        static void Main(string[] args)
        {
            var root = new Node("root");
            root.Add(new Node("1st child"));
            root.Add(new Node("2nd child"));
            root.Add(new Node("3rd child"));

            var firstchild = root["1st child"];

            foreach (var child in root)
            {
                Console.WriteLine(child);
            }
        }
    }
}

Upvotes: 0

Mark Seemann
Mark Seemann

Reputation: 233317

That looks a lot like a Tree, although I usually model this more explicitly with a Tree class like this one:

public class Tree<T>
{
    public T Item { get; set; }

    public IList<T> Children { get; }
}

Your code seems like a combination of a Tree and a Composite, although you are not quite there since the enumerator doesn't include the 'root' item.

The design, at it is currently presented, seems slightly off. I would either pull it more in the direction of an explicit Tree like above, or towards a true Composite.

Upvotes: 2

Related Questions