Click here to Skip to main content
15,884,177 members
Articles / Programming Languages / C#
Article

UniqueStringList

Rate me:
Please Sign up or sign in to vote.
3.00/5 (4 votes)
21 Nov 20024 min read 63K   536   26   9
A C# class to store and manipulate lists of unique strings

Introduction

UniqueStringList is a class to store and manipulate lists of strings that must be unique. Strings can be added and removed, inserted, sorted, looked up, compared to other lists etc. but duplicate strings will be simply ignored and will not raise an exception.

Background

Whilst writing a VB6 Active Directory toolkit library for a client, I wrote a VB class called UniqueStringCollection to allow groups of case-insensitive strings, which had to be unique, to be stored together and manipulated (for LDAP values/attribute names/list of DNs and the like).

I found this class to be useful in so many situations that when I started looking at C#, it was one of the first classes in my utility library.

I decided to use the process of making a port of this class a backgrounder for myself in best .NET/C# practices such as inheritance, interfaces, nested classes, class wrappers, serialization etc.

This article (my first, so go easy on me!) describes that ported version, since renamed to UniqueStringList for reasons we will see shortly.

Defining the Requirements

The basic requirements of my VB class were incorporated but expanded to make use of interfaces such as IList which would allow better integration into the .NET framework hence the renaming to UniqueStringList. These were my requirements:-

  • Only one instance of a given string allowed in the list
  • Duplicate strings are ignored with no exception thrown
  • Lookups must be fast
  • Nulls cannot be contained in the list (but no exception thrown)
  • Implement IList/ICollection/ISerializable to be usable with other framework components
  • Make a case-sensitive version available
  • Contents can be sorted/reversed etc.
  • Include a flag to allow/disallow empty strings
  • Include an option to produce a formatted summary list of the contents
  • Make read-only and synchronized versions of the list available

How it Works

Although the existing .NET framework collections are comprehensive, no single type meets all of the requirements as specified above. However, by combining an ArrayList (for the IList implementation) and a Hashtable (to provide quick lookups) all of the above requirements can be met.

If you just need to have a collection of strings in no particular order then the Add() and AddRange() methods can be used to append strings to the list. Alternatively, if more control is required over the ordering of the strings, then use Insert which allows an index to be specified.

UniqueStringList supports all of the usual framework collection list-type features including a read/write indexer, IndexOf, Clear, Contains, along with ArrayList type features such as Sort, Reverse, ToArray.

One feature that is not immediately obvious is the EmptyAlias property. This is a read/write property that get/sets a string to be used in place of an empty string. This is my solution to the requirement of allowing or disallowing empty strings. By default, EmptyAlias is itself an empty string and so an empty string will be allowed in the list. By setting it to null, empty strings will be translated to nulls and so be ignored. You can also set it to an arbitrary string and that string will be used in place of "" (I originally had a vague notion of using a UniqueStringList as a data source to a combo box and using this feature to supply a special value such as "(none)"). This seems to be a more useful solution than just using a boolean flag.

The read-only version of the list is implemented as a wrapper class. This will allow a class to return a UniqueStringList reference that is not externally modifiable but is 'live' and not just a snapshot copy. To create a read-only wrapper, call the UniqueStringList.ReadOnly static method passing a reference to the UniqueStringCollection you want to wrap.

The synchronized version of the list is obtained in the same way - call the static UniqueStringList.Sync method.

Finally, for completeness, I added a case-sensitive version as a nested class. Use

UniqueStringList myCaseSensitiveList = 
new UniqueStringList.CaseSensitive();
to create an instance.

Samples

Here is a simplistic snippet of code just to get a feel for the class - I'm sure you will find more interesting uses for it.

C#
UniqueStringList myList = new UniqueStringList();

myList.Add("cat");
myList.AddRange(new string[] {"dog", "parrot"});

Console.WriteLine(myList.ToString("My pets: ", "; ", "."));

A bit boring? Well OK, here's another more realistic sample:

C#
string[] mandatoryAttributes = new string[] {
    "objectClass", "objectCategory"
};

// Create a list to hold the attribute names to find and 
// populate it with the mandatory attribute
UniqueStringList attributeNames = 
    new UniqueStringList(mandatoryAttributes);

// Let each custom filter add in the attribute names that 
// they require
foreach(CustomFilter filter in filters) {
    attributeNames.Add(filter.RequiredAttributes);
}

Points of Interest

Check out in the source code how the wrapper classes call an internal constructor with a superfluous parameter (just used to differentiate from the other constructors). This allows the wrapper class to be created without initializing its own data fields to save memory though I must confess I found this idea from the Rotor source code.

By allowing additional formatting parameters on the ToString() method override, it is easy to make summary lists using commas or semi-colons or CRLF combinations etc.

C#
// Returns:
//   [item1. item2. item3]
myList.ToString("[", "; ", "]");

// Returns:
//   item1
//   item2
//   item3
myList.ToString("", "\n", "");

Sometimes, it can be handy to see the status of a class during debugging. By creating a property that is only compiled into a debug build, you can have the benefit of seeing a summary in the watch window whilst debugging with no overhead in a release build.

C#
#if DEBUG
public string DebugWatch {
  get { return ToString(); }
}
#endif

Future Enhancements

  • Add class and method documentation - need to check out NDoc which I hear is excellent.

History

  • v1.00 22/11/02 - First release to CodeProject.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Hunton Information Systems Ltd.
United Kingdom United Kingdom
Simon Hewitt is a freelance IT consultant and is MD of Hunton Information Systems Ltd.

He is currently looking for contract work in London.

He is happily married to Karen (originally from Florida, US), has a lovely daughter Bailey, and they live in Kings Langley, Hertfordshire, UK.

Comments and Discussions

 
GeneralBug with case-insensivity Pin
Michael Ballou1-May-03 13:50
Michael Ballou1-May-03 13:50 
GeneralRe: Bug with case-insensivity Pin
Michael Ballou1-May-03 14:02
Michael Ballou1-May-03 14:02 
QuestionCould I use this for ... ? Pin
davidqxo2-Feb-03 4:47
davidqxo2-Feb-03 4:47 
GeneralGot a Question... Pin
David Stone23-Nov-02 5:06
sitebuilderDavid Stone23-Nov-02 5:06 
GeneralRe: Got a Question... Pin
SimmoTech23-Nov-02 5:36
SimmoTech23-Nov-02 5:36 
GeneralRe: Got a Question... Pin
David Stone23-Nov-02 10:23
sitebuilderDavid Stone23-Nov-02 10:23 
GeneralRe: Got a Question... Pin
BrooksV16-Oct-03 14:23
BrooksV16-Oct-03 14:23 
GeneralRe: Got a Question... Pin
PIEBALDconsult6-Nov-06 6:20
mvePIEBALDconsult6-Nov-06 6:20 
GeneralRe: Got a Question... Pin
SimmoTech6-Nov-06 22:56
SimmoTech6-Nov-06 22:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.