Partager via


First stab at the basic ICollection interface done.

Ended up with the following for my ICollection interface:

namespace Testing.Collections

{

    public interface ICollection<A>

    {

        /// <summary>

        /// Attempts to add an element into this collection

        /// </summary>

        /// <param name="element">The element to add into this collection</param>

        /// <returns>true if the element was successfully added, false otherwise</returns>

        bool Add(A element);

        /// <summary>

        /// Returns true if this collection is Empty, false otherwise.

        /// </summary>

        /// <value></value>

        bool Empty

        {

            get;

        }

        /// <summary>

        /// Removes all the elements contained in this collection.

        /// </summary>

        /// <exception>A NotSupportedException is thrown if the collection cannot be cleared</exception>

        void Clear();

        /// <summary>

        /// Determines whether an element is in this collection

        /// </summary>

        /// <param name="element">The element to locate in this collection</param>

        /// <returns>Returns true if the element is contained within this collection.

        /// determining containment is left up to the concrete implementation

        /// to define.</returns>

        bool Contains(A element);

        /// <summary>

        /// Removed the first occurance of the specified element from this collection

        /// </summary>

        /// <param name="element">The element to remove</param>

        /// <returns>Returns true if the element is contained within this collection and

        /// subsequently removed. Determining containment is left up to the concrete

        /// implementation to define.</returns>

        bool Remove(A element);

    }

}

The next issue on the plate is how to get to the elements in the collection.  I stated earlier that i don't necessarily want to limit myself to finite collections, so indexing into the collection seems out.  The only other option i see it some way to query collection for all of it's elements one at a time.  i.e. implementing the Iterator pattern (see Design Patterns).  However, there are several choices you can make (with corresponding tradeoffs).  The first thing I need to decide on is if these collections are going to support internal or external iteration (or both).  External iteration is done with the pattern we commonly see in the BCL.  I.e.

IEnumerator<A> GetEnumerator();

Calling GetEnumerator returns an IEnumerator that one can foreach across to access all elements.  Alternatively we can use internal iteration which looks like:

void Iterate(Predicate<A> p);

When you call this you pass a delegate that will be performed on each element in the list.  The delegates return value says if we should continue.  Returning true is equivalent to “continue”ing in a foreach, returning false is equivalent to “break”ing out of a foreach.  I might also provide:

void Iterate(Operator<A> p);

Which will iterate over every element without stopping. An example of using both is shown here:

            foreach (int i in collection)

            {

                Console.WriteLine(i);

           }

            collection.Iterate(delegate(int i) {

                Console.WriteLine(i);

            });

Or, we could have something like:

            foreach (int i in collection)

            {

                Console.WriteLine(i);

                if (i > 10)

                {

                    break;

                }

            }

            collection.Iterate(delegate(int i) {

                Console.WriteLine(i);

                return i <= 10;

            });

I'm not sure which i prefer, although I am strongly leaning toward Iterate.  It allows for far easier implementation of the Iterator (try implementing GetEnumerator on a tree versus Iterate on a tree).  However, you are more limited in what you can do.  For example, if sublasses return specialized IEnumerators (like IListEnumerator or ITreeEnumerator) then this can't really be expressed well in the Iterate model.  Also, because you are iterating all at once, it makes it difficult to weave enumerators together.  (try writing ArrayCollection.Equals with just Iterate versus ArrayCollection.Equals using GetEnumerator).

Comments

  • Anonymous
    May 16, 2004
    If the Add method may return without having added the element, it must be called TryAdd.
  • Anonymous
    May 16, 2004
    The Clear method is the only method that can throw an exception, but I would expect that some of the others are more likely candidates to throw, yet these return values. Why the distinction? Under what conditions can you fail to clear a collection?
  • Anonymous
    May 17, 2004
    Empty can be a verb, so it can be read to mean empty the collection, like Clear, better change it to IsEmpty.

    And don't throw NotSupportedException exception from Clear, just return true or false.
  • Anonymous
    May 17, 2004
    The comment has been removed
  • Anonymous
    May 17, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    I assume you mean ``an "Add" that throws and a TryAdd which returns a bool''.

    I am not suggesting that you should have both an Add and a TryAdd. I am just saying that if you pick the "TryAdd" semantics, then you should use the name "TryAdd", IMHO.

    If you feel that you must have a method called "Add" in your interface, then that method must comply with "Add" semantics, i.e. if the call returns, then the element was added. I cannot see why one would insist on a method with a particular name, without also insisting on a particular semantics.
  • Anonymous
    May 31, 2009
    PingBack from http://patiochairsite.info/story.php?id=370