473,761 Members | 5,163 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Framework Bug: KeyedCollection <T>

KeyedCollection is a very handy little class, that unforutnatly has a nasty
bug in it.

The bug (which I ran across) causes the following code to fail:

if (!rooms.Contain s(room))
_rooms.Add(room );

The problem is that contains returns "false", but then Add throws an
exception because the item really is already in there.

The follow code illustrates the bug in the class:

static void Main(string[] args)
{
KeyTest foo = new KeyTest();
KeyedClass item1 = new KeyedClass("1", "99");
KeyedClass item2 = new KeyedClass("1", "99");
KeyedClass item3 = new KeyedClass("1", "100");

foo.Add(item1);

// this one fails
if (!foo.Contains( item2))
Console.WriteLi ne("Broken Based on Instancing!");

// this one fails
if (!foo.Contains( item3))
Console.WriteLi ne("Broken Based on Key Lookup!");

// this one works
if (!foo.Contains( item2.Key))
Console.WriteLi ne("Even Contains by Key Type is Broken");
}

class KeyTest : KeyedCollection <string, KeyedClass>
{
protected override string GetKeyForItem(K eyedClass item)
{
return item.Key;
}
}

class KeyedClass
{
public readonly string Key, Value;
public KeyedClass(stri ng k, string v)
{
Key = k; Value = v;
}
}

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins
Apr 17 '07 #1
10 3788
I should add that there's a straightforward workaround. Changing the
concrete KeyedCollection class to override the base Contains method works
pretty well. Unfortunatly, very few people are going to think to do this
every time they create a keyed collection class...

class KeyTest : KeyedCollection <string, KeyedClass>
{
protected override string GetKeyForItem(K eyedClass item)
{
return item.Key;
}

public new bool Contains(KeyedC lass item)
{
return this.Contains(i tem.Key);
}
}

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

"Chris Mullins [MVP]" <cm******@yahoo .comwrote in message
news:u6******** ******@TK2MSFTN GP02.phx.gbl...
KeyedCollection is a very handy little class, that unforutnatly has a
nasty bug in it.

The bug (which I ran across) causes the following code to fail:

if (!rooms.Contain s(room))
_rooms.Add(room );

The problem is that contains returns "false", but then Add throws an
exception because the item really is already in there.

The follow code illustrates the bug in the class:

static void Main(string[] args)
{
KeyTest foo = new KeyTest();
KeyedClass item1 = new KeyedClass("1", "99");
KeyedClass item2 = new KeyedClass("1", "99");
KeyedClass item3 = new KeyedClass("1", "100");

foo.Add(item1);

// this one fails
if (!foo.Contains( item2))
Console.WriteLi ne("Broken Based on Instancing!");

// this one fails
if (!foo.Contains( item3))
Console.WriteLi ne("Broken Based on Key Lookup!");

// this one works
if (!foo.Contains( item2.Key))
Console.WriteLi ne("Even Contains by Key Type is Broken");
}

class KeyTest : KeyedCollection <string, KeyedClass>
{
protected override string GetKeyForItem(K eyedClass item)
{
return item.Key;
}
}

class KeyedClass
{
public readonly string Key, Value;
public KeyedClass(stri ng k, string v)
{
Key = k; Value = v;
}
}

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

Apr 17 '07 #2
Chris Mullins [MVP] <cm******@yahoo .comwrote:
KeyedCollection is a very handy little class, that unforutnatly has a nasty
bug in it.
Hmm... I'm not sure.
The bug (which I ran across) causes the following code to fail:

if (!rooms.Contain s(room))
_rooms.Add(room );

The problem is that contains returns "false", but then Add throws an
exception because the item really is already in there.
Indeed.

The Contains method is meant to check whether the given *key* is in the
collection. The docs say that GetKeyForItem is used to look up the key
for each element, but there's nothing that state it's used to get the
key for the argument you pass into Contains - that's meant to already
be a key. In your case, it isn't a key, it's an item.

On the other hand, I'm surprised this compiles, given that the type you
pass to Contains is meant to be of type TKey, and in your sample code
you're passing in an instance of TItem.

Hmm. Will look closer when I have more time.

--
Jon Skeet - <sk***@pobox.co m>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Apr 17 '07 #3
Chris,
>KeyedCollectio n is a very handy little class, that unforutnatly has a nasty
bug in it.
I don't think it's a bug. You have to override Equals in the
KeyedClass or provide a custom IEqualityCompar er to the
KeyedCollection for Contains to work as expected.

Note that the first two calls to Contains end up calling
Collection<T>.C ontains in the base class, which "determines whether an
element is in the Collection" (i.e. requires equality comparison). The
last call on the other hand is to KeyedCollection <TKey,
TItem>.Contains which "determines whether the collection contains an
element with the specified key".
Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Apr 17 '07 #4
"Jon Skeet [C# MVP]" <sk***@pobox.co mwrote in message
news:MP******** *************** *@msnews.micros oft.com...
On the other hand, I'm surprised this compiles, given that the type you
pass to Contains is meant to be of type TKey, and in your sample code
you're passing in an instance of TItem.
That's acutally the underlying problem - The Contains<TItemi s a public
method provided by the base Collection<Tcla ss. The Contains<TKeyme thod
is provided by the KeyedCollection class.

This means there are two overloads in my sample:
Contains(string key)
Contains(KeyedC lass item)

If I call the Contains(string ) method, everything is fine - the keyed
collection does it's thing and it works properly. If I called the
Contains(KeyedC lass) method, then the Collection<Tcla ss is called
(bypassing all the checked in the KeyedCollection class) and ends up doing a
List.Contains, which is the wrong thing altogether.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins
Apr 17 '07 #5
I'm clear on the behavior as to what's really going on, but once I figured
out it works this way, I found buggy code all over the place.

In many spots, code written by pretty sharp folks was calling
if (items.Containt s(Item)) { do something }

.... and it wasn't doing what they thought it would.

Based on "What should this do?", code that looks like (and common variations
on):
if (!rooms.Contain s(room))
_rooms.Add(room );

.... really needs to work "right", and not throw an exception.

I realize overriding equals would do the trick here, but for a class called
"KeyedCollectio n", only the Key should need the equals operator.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

"Mattias Sjögren" <ma************ ********@mvps.o rgwrote in message
news:uS******** ******@TK2MSFTN GP03.phx.gbl...
Chris,
>>KeyedCollecti on is a very handy little class, that unforutnatly has a
nasty
bug in it.

I don't think it's a bug. You have to override Equals in the
KeyedClass or provide a custom IEqualityCompar er to the
KeyedCollection for Contains to work as expected.

Note that the first two calls to Contains end up calling
Collection<T>.C ontains in the base class, which "determines whether an
element is in the Collection" (i.e. requires equality comparison). The
last call on the other hand is to KeyedCollection <TKey,
TItem>.Contains which "determines whether the collection contains an
element with the specified key".
Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

Apr 17 '07 #6
Chris Mullins [MVP] <cm******@yahoo .comwrote:
"Jon Skeet [C# MVP]" <sk***@pobox.co mwrote in message
news:MP******** *************** *@msnews.micros oft.com...
On the other hand, I'm surprised this compiles, given that the type you
pass to Contains is meant to be of type TKey, and in your sample code
you're passing in an instance of TItem.

That's acutally the underlying problem - The Contains<TItemi s a public
method provided by the base Collection<Tcla ss. The Contains<TKeyme thod
is provided by the KeyedCollection class.
Right. Fair enough.
This means there are two overloads in my sample:
Contains(string key)
Contains(KeyedC lass item)

If I call the Contains(string ) method, everything is fine - the keyed
collection does it's thing and it works properly. If I called the
Contains(KeyedC lass) method, then the Collection<Tcla ss is called
(bypassing all the checked in the KeyedCollection class) and ends up doing a
List.Contains, which is the wrong thing altogether.
And once more, ill-thought-out inheritance strikes :(

--
Jon Skeet - <sk***@pobox.co m>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Apr 17 '07 #7
I submitted this as a bug to Microsoft, and they have verified it and
accepted it. It's Issue ID 271542, and can be tracked here:
http://connect.microsoft.com/VisualS...dbackID=271542

"We have reproduced this bug on WinXP pro SP2 and VSTS2005 SP1, and we are
sending this bug to the appropriate group within the Visual Studio Product
Team for triage and resolution."

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

"Chris Mullins [MVP]" <cm******@yahoo .comwrote in message
news:u6******** ******@TK2MSFTN GP02.phx.gbl...
KeyedCollection is a very handy little class, that unforutnatly has a
nasty bug in it.

The bug (which I ran across) causes the following code to fail:

if (!rooms.Contain s(room))
_rooms.Add(room );

The problem is that contains returns "false", but then Add throws an
exception because the item really is already in there.

The follow code illustrates the bug in the class:

static void Main(string[] args)
{
KeyTest foo = new KeyTest();
KeyedClass item1 = new KeyedClass("1", "99");
KeyedClass item2 = new KeyedClass("1", "99");
KeyedClass item3 = new KeyedClass("1", "100");

foo.Add(item1);

// this one fails
if (!foo.Contains( item2))
Console.WriteLi ne("Broken Based on Instancing!");

// this one fails
if (!foo.Contains( item3))
Console.WriteLi ne("Broken Based on Key Lookup!");

// this one works
if (!foo.Contains( item2.Key))
Console.WriteLi ne("Even Contains by Key Type is Broken");
}

class KeyTest : KeyedCollection <string, KeyedClass>
{
protected override string GetKeyForItem(K eyedClass item)
{
return item.Key;
}
}

class KeyedClass
{
public readonly string Key, Value;
public KeyedClass(stri ng k, string v)
{
Key = k; Value = v;
}
}

--
Chris Mullins, MCSD.NET, MCPD:Enterprise , Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

Apr 18 '07 #8

"Jon Skeet [C# MVP]" <sk***@pobox.co mschreef in bericht
news:MP******** *************** *@msnews.micros oft.com...
Chris Mullins [MVP] <cm******@yahoo .comwrote:
>"Jon Skeet [C# MVP]" <sk***@pobox.co mwrote in message
news:MP******* *************** **@msnews.micro soft.com...
On the other hand, I'm surprised this compiles, given that the type you
pass to Contains is meant to be of type TKey, and in your sample code
you're passing in an instance of TItem.

That's acutally the underlying problem - The Contains<TItemi s a public
method provided by the base Collection<Tcla ss. The Contains<TKey>
method
is provided by the KeyedCollection class.

Right. Fair enough.
>This means there are two overloads in my sample:
Contains(strin g key)
Contains(Keyed Class item)

If I call the Contains(string ) method, everything is fine - the keyed
collection does it's thing and it works properly. If I called the
Contains(Keyed Class) method, then the Collection<Tcla ss is called
(bypassing all the checked in the KeyedCollection class) and ends up
doing a
List.Contain s, which is the wrong thing altogether.

And once more, ill-thought-out inheritance strikes :(
Why would that be? Depending on the context of your quest for existance it's
good that both exist.
If you have two collections between which you move items based on user input
or whatever you want to do, you may want to check for existence of the item
reference rather than by key. Although checking for existence by key would
probably faster since the internal dictionairy then would be used. Though if
there are not enough items in the collection to have enabled the internal
dictionary, searching by reference would be faster.
>
--
Jon Skeet - <sk***@pobox.co m>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Apr 21 '07 #9
<"MagicBox" <avh^at^runbox. com>wrote:
If I call the Contains(string ) method, everything is fine - the keyed
collection does it's thing and it works properly. If I called the
Contains(KeyedC lass) method, then the Collection<Tcla ss is called
(bypassing all the checked in the KeyedCollection class) and ends up
doing a
List.Contains, which is the wrong thing altogether.
And once more, ill-thought-out inheritance strikes :(

Why would that be? Depending on the context of your quest for existance it's
good that both exist.
Possibly - but the fact that they're both called the same thing makes
it confusing. Heck, if it can confuse Chris M, it can confuse anyone.

Chris's complaint that:

if (!rooms.Contain s(room))
rooms.Add(room) ;

can break is a perfectly valid one, IMO. The type isn't clear whether
it views the collection as one of keys, or one of values, effectively.

--
Jon Skeet - <sk***@pobox.co m>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Apr 21 '07 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
1978
by: Jim Fischer | last post by:
Paragraph 10.3/2 in the C++ standard has the following code example: <quote> struct A { virtual void f(); }; struct B : virtual A { virtual void f(); };
1
3175
by: Mike Strieder | last post by:
How can i get the text of the System.Type e.g. "base64Binary" from the .Net type "byte" I can not found any Function to give back this Schematype as string. thx for your help
5
1336
by: Hendrik Schober | last post by:
Hi, I had originally suspected this to be a bug in the std lib, that's why I started a thred in the STL newsgroup. However, it seems as if it is a compiler bug, so I'm transfering it to here. The original code was this: ----- Original Message -----
8
1639
by: Mark | last post by:
We have a multi-line textbox that users copy and paste email text into. The pasted text frequently will contain a tag like <blah@aol.com> or similar. I believe .NET is protecting itself from code injection by throwing a global error when this occurs. The exception message is pasted below. We will NOT be able to train our users to eliminate all <> tags. What's the best way to deal with this issue? Thanks in advance.
8
1365
by: active | last post by:
I use quickwatch on (astrThisOne <> "") and it reports: False as it should because astrThisOne reports: "" Yet If (astrThisOne <> "") Then executes the Then clause
3
2584
by: Dave Booker | last post by:
Am I missing something here? It looks like the generic Queue<T> implements Enumerable<T> and ICollection but not ICollection<T>. (I want to use it in an interface that wants an ICollection<T>.) Is there a reason for this, or is it just an oversight in .NET 2.0? Is there a computationally easy way to cast/convert a Queue<T> to an ICollection<T>?
6
1724
by: Patrick Jox | last post by:
Hi, I have a client request to build an asp.net application. This application shall be installed on a machine running Windows NT 4.0 SP 6a. As far as I found out framework 2.0 may not installed on any NT versions. But I hope I can at least use ASP.NET 2.0 in combination with Framework 1.1 and Visual Studio 2003. This should work but. - I do not want to convert my older projects (that are still productive) to ASP.NET 2.0
4
5718
by: Dave Booker | last post by:
So did the .NET 2.0 working group just run out of steam before it got to a ReadOnlyDictionary<> wrapper? I am publishing events containing a SortedList<>, and obviously I don't want event handlers to be able to do anything to the collection at the same time other recipients could be handling it. I found a ReadOnlyCollection<> wrapper, which I can apply to the .Keys and ..Values of the SortedList, but I can't find any way to wrap the...
11
4101
by: raylopez99 | last post by:
Keep in mind this is my first compiled SQL program Stored Procedure (SP), copied from a book by Frasier Visual C++.NET in Visual Studio 2005 (Chap12). So far, so theory, except for one bug (feature?) below. At some point I'm sure I'll be able to laugh about this, akin to forgeting a semi-colon in C/C++, but right now it's frustrating (time to sleep on it for a while). Problem-- For some reason I get the error when trying to save files...
0
9522
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9948
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9902
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9765
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8770
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7327
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6603
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5364
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3866
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.