Solved

Instantiating an ICollection

Posted on 2004-04-21
7
928 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
  • 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
How our DevOps Teams Maximize Uptime

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us. Read the use case whitepaper.

 
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

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

A basic question.. “What is the Garbage Collector?” The usual answer given back: “Garbage collector is a background thread run by the CLR for freeing up the memory space used by the objects which are no longer used by the program.” I wondered …
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

856 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