Solved

Instantiating an ICollection

Posted on 2004-04-21
7
929 Views
Last Modified: 2008-02-01
In the line marked ** I would like to instantiate a container supporting ICollection. I don't want to hardcode a choice of container here. I'd like the container choice to be something that can be changed without changing the code in the class. I was thinking of calling a container factory class method on an external class to get an instantiated ICollection but I'm not sure I have this well thought out.

class foo 'basically a list of lists.
  private _containerOfContainers : IDictionary

  public sub addItem(sKey, sValue)
   
   if not _containerOfContainers.contains(sKey) then
        dim oNewChildContainer as ICollection
**        oNewChildContainer = new ???
       _containerOfContainers.Add(sKey, oNewChildContainer )
    endif
   _containerOfContainers(sKey).Add(sValue);

  end sub

Perhaps something like:

Public class AbstractContainerProvider {
  public shared ConcreteContainerProvider;
  public function ProvideNewContainer() as Icollection {
   return ConcreteContainerProvider._ProvideNewContainer();
  };
 
 protected mustoverride function _ProvideNewContainer();

end class

public class ConcreteContainerProviderHashTable implements AbstractContainerProvider
 protected function _ProvideNewContainer() {
  return new HashTable();
 }

This would be used as follows:
  AbstractContainerProvider.ConcreteContainerProvider = new  ConcreteContainerProviderHashTable()

and my addItem method would then call the abstract factory method to get a new container:

  oNewChildContainer = AbstractContainerProvider.ProvideNewContainer();

That should mean I can change the concrete container used without recompiling my foo class.

Comments please.
0
Comment
Question by:monosodiumg
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 2
7 Comments
 
LVL 22

Expert Comment

by:_TAD_
ID: 10879313


Set it to null.   Then when you decide which object to set it to, you can do that without a problem.


If null doesn't work for some reason (you can't add a null value), then use a generic object

oNewChildContainer = new Object();
0
 
LVL 22

Expert Comment

by:_TAD_
ID: 10879396

oh wait....  what was I thinking, oNewChildContainer is an ICollection object so you need something that inherits ICollection.

forget the last piece about 'new Object()'
0
 
LVL 22

Expert Comment

by:_TAD_
ID: 10879568


Why not just create a new object that inherits from ICollection?

As long as you pull the data out using ICollection and enumerate through it, it doesn't really matter what kind of object you use.

// Hash Table
                  Hashtable myHash = new Hashtable();
                  myHash.Add("1","One");
                  myHash.Add("2","Two");
                  ICollection col1 = myHash;
// Array List            
                  ArrayList myList = new ArrayList();
                  myList.Add("Three");
                  myList.Add("Four");
                  ICollection col2 = myList;

                  Console.WriteLine(col1.Count.ToString());
                  Console.WriteLine(col2.Count.ToString());

They both work exactly the same way.
0
MIM Survival Guide for Service Desk Managers

Major incidents can send mastered service desk processes into disorder. Systems and tools produce the data needed to resolve these incidents, but your challenge is getting that information to the right people fast. Check out the Survival Guide and begin bringing order to chaos.

 
LVL 22

Expert Comment

by:_TAD_
ID: 10879853


Here is a generic setup which shows how you can use any generic object as long as it inherits ICollection.




                  Hashtable myHash = new Hashtable();
                  myHash.Add("HT1","HT One");
                  myHash.Add("HT2","HT Two");
                  myHash.Add("HT3","HT Three");
                  myHash.Add("HT4","HT Four");
                  
                  ArrayList myList = new ArrayList();
                  myList.Add("AL 1");
                  myList.Add("AL 2");
                  myList.Add("AL 3");
                  myList.Add("AL 4");
                  
//Create ICollection object
                  ICollection ic;
                  if (DateTime.Now.Second%2>0)
                        ic = myList;    // Array List
                  else
                        ic = myHash;  // Hash Table

                  IEnumerator ie = ic.GetEnumerator();
                  


In all collection objects that I know of, all data is either stored directly into the collection (using an integer to enumerate through the collection), or the collection enumerator is stored as a dictionary entry)  The following handles both types.




                  ie.MoveNext();
                  for(int i=0;i<ic.Count;i++)
                  {
                        if (ie.Current.GetType()==typeof(DictionaryEntry))
                              Console.WriteLine(((DictionaryEntry) ie.Current).Value.ToString());
                        else
                              Console.WriteLine(ie.Current.ToString());

                        ie.MoveNext();
                  }
0
 
LVL 12

Author Comment

by:monosodiumg
ID: 10880237
_TAD_,
I don't understand how your posts address the problem. Reread my first paragraph. It requires the you be able to supply different containers withouth changing the existing code.
If I go your way (section starting //Create ICollection object
) and  next year come up with MySuperCoolCollection that supports ICollection, I would have to change the code and recompile in order to be able to use it. There are 100s of classes out there that support ICollection. Writing an extensive list if..then.. tree also means importing all the classes that are mentioned in that tree. The number of dependencies is then huge and is any of the classes chamge I would have to recompile.
I'm looking for a way for avoiding this.

I've come up with the solution below but I'm not very confident about it. It does seem to work. It allows you to come up new collections and my collection using class cn use these without recompilation.

Public Interface ICollectionGenerator
  Function GenerateICollection() As ICollection
End Interface

Public Class ICollectionGeneratorHashtable
  Implements ICollectionGenerator
  Private _ncapacity As Integer = -1
  Private _snglLoadFactor As Single = -1.0

  Public Sub New()
    _ncapacity = -1
    _snglLoadFactor = -1.0
  End Sub

  Public Sub New(ByVal nCapacity As Integer)
    _ncapacity = nCapacity
    _snglLoadFactor = -1
  End Sub

  Public Sub New(ByVal nCapacity As Integer, ByVal snglLoadFactor As Single)
    _ncapacity = nCapacity
    _snglLoadFactor = snglLoadFactor
  End Sub

  Function GenerateICollection() As ICollection Implements ICollectionGenerator.GenerateICollection
    Dim oHashTable As Hashtable
    If (_ncapacity > 0) Then
      If _snglLoadFactor > 0 Then
        oHashTable = New Hashtable(_ncapacity, _snglLoadFactor)
      Else
        oHashTable = New Hashtable(_ncapacity)
      End If
    Else
      oHashTable = New Hashtable()
    End If
    Return oHashTable
  End Function
End Class

Public Class ICollectionGeneratorArrayList
  Implements ICollectionGenerator

  Private _ncapacity As Integer = -1
  Public Sub New()
    _ncapacity = -1
  End Sub

  Public Sub New(ByVal nCapacity As Integer)
    _ncapacity = nCapacity
  End Sub

  Function GenerateICollection() As ICollection Implements ICollectionGenerator.GenerateICollection
    Dim oArrayList As ArrayList

    If (_ncapacity > 0) Then
      oArrayList = New ArrayList(_ncapacity)
    Else
      oArrayList = New ArrayList()
    End If

    Return oArrayList
  End Function

End Class

The client class now looks like this:
Public class ICollectionConsumer
  Private _ICollectionGenerator As ICollectionGenerator

  public sub New(oICollectionGenerator as ICollectionGenerator)
    _ICollectionGenerator  = oICollectionGenerator
  end sub

  public sub Foo()
   Dim MyUnknownCOllectionImplementation as ICollection = _ICollectionGenerator.GenerateICollection()
  ...
  end sub

end class

That class is instantiated with a specific ICollectionGenerator:
New Foo(new ICollectionGeneratorHashtable())
or
New Foo(new ICollectionGeneratorArrayList())
and so on.

That means I can create a new ICollectionGenerator subclass for MySuperNewCollection and I do not need to recompile Foo for Foo to be able to use it.

I suppose this is really a design pattern question. I haven't got the GoF book around otherwise I'd  probably never have asked the question. I will have it tomorrow :)
mono
 

0
 
LVL 22

Accepted Solution

by:
_TAD_ earned 250 total points
ID: 10881156

Mono>

Your proposed solution is better than the quick hack I suggested.  But as for addressing the problem...  In my experience, a collection is enumerated by one of two things, either a number or some object (usually a string, but not always).


This code:


               ie.MoveNext();
               for(int i=0;i<ic.Count;i++)
               {
                    if (ie.Current.GetType()==typeof(DictionaryEntry))
                         Console.WriteLine(((DictionaryEntry) ie.Current).Value.ToString());
                    else
                         Console.WriteLine(ie.Current.ToString());

                    ie.MoveNext();
               }


enumerated through your generic collection object.  If the collection is enumerated by number, then you can simply use ie.current.   If on the other hand the collection is enumerated by an object, then you need to take the dictionary entry of that object to pull the value.  The concept is sound, and it functions perfectly for the short example I have provided.  However, I don't know how extensible it is.  It works well for all of the .Net classes I've tried it with, but that doesn't mean it won't choke on one of your user-defined classes.
0
 
LVL 12

Author Comment

by:monosodiumg
ID: 10882626
>either a number or some object
The only thing that list excludes is Boolean so I'm not sure what the point is.

The problem I have is in instantiating the collection, not in reading it. Whatever code reads the collection will have some defnite expectations as to what that collection contains and will be cast accordingly.

As an aside, I'm not at all happy with the built-in collections because of their lack of typing. I will at some point replace those with stronlgy types collections, which will make the casts redundant but guarantee success. C#2.0 will remedy that I believe. VS2003 I think provides a built-in typed collection generator.

>In all collection objects that I know of, all data is either stored directly into the collection (using an integer to enumerate through the collection), or the collection enumerator is stored as a dictionary entry)
Youalso elsehwer ekeep referring to enumertaing by numbers. Do you mean indexing? E.g. myArray[5]? That is completly different to enumerators. The builtin Enumerators  are untype (their Current is Object). No numbers involved. I think you must be thinking of things that implement IList, where you can access an object by position. If all I have is ICollection, which by inheritance implies IEnumerable, then I can't access by position.

>doesn't mean it won't choke on one of your user-defined classes
I'm guaranteeing no choking by sticking to the interfaces.
0

Featured Post

Guide to Performance: Optimization & Monitoring

Nowadays, monitoring is a mixture of tools, systems, and codes—making it a very complex process. And with this complexity, comes variables for failure. Get DZone’s new Guide to Performance to learn how to proactively find these variables and solve them before a disruption occurs.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Are you ready to implement Active Directory best practices without reading 300+ pages? You're in luck. In this webinar hosted by Skyport Systems, you gain insight into Microsoft's latest comprehensive guide, with tips on the best and easiest way…

740 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question