Generic IDictionary confusion

  • Thread starter Thread starter vcquestions
  • Start date Start date
V

vcquestions

trying to design an effiecient interface, where I can pass an
IDictionary<int,string^> to a method. int is some id and string^ is
some
value that is initialized to null by a caller.

The callee function needs to update the value based on the key.

in native code I'd have passed a std::map and do ((*it).second) = new
std::string("test");

I don't see a way to do it in managed code.

I obviously can't assign it to keyValuePair element as it's read only.
And I can't even do this:

for each ( KeyValuePair<int, string^>^ iter in testCollection )
{
testCollection[iter->key] = "test"
}

as on the second pass of the loop, the code above will throw since the
colleciton is modified.

Thus, it looks like I need 2 passes, where I'd make a list of ids in
the first pass and on the second pass, I'd do something
like:
for ( int i = 0; i < ListIds.Count; ++i )
{
testCollection[listIds] = "test";
}

This code seems very poor. Is there really no way to update the
IDictionary<key,value> in 1 pass? I've got to be missing some
IEnumerator that would let me do that :)

Thanks in advance!
vcq
 
trying to design an effiecient interface, where I can pass an
IDictionary<int,string^> to a method. int is some id and string^ is
some
value that is initialized to null by a caller.

The callee function needs to update the value based on the key.

in native code I'd have passed a std::map and do ((*it).second) = new
std::string("test");

I don't see a way to do it in managed code.

I obviously can't assign it to keyValuePair element as it's read only.
And I can't even do this:

for each ( KeyValuePair<int, string^>^ iter in testCollection )
{
testCollection[iter->key] = "test"

}

as on the second pass of the loop, the code above will throw since the
colleciton is modified.

Thus, it looks like I need 2 passes, where I'd make a list of ids in
the first pass and on the second pass, I'd do something
like:
for ( int i = 0; i < ListIds.Count; ++i )
{
testCollection[listIds] = "test";

}

This code seems very poor. Is there really no way to update the
IDictionary<key,value> in 1 pass? I've got to be missing some
IEnumerator that would let me do that :)

Thanks in advance!
vcq


any suggestions?
 
vcquestions said:
trying to design an effiecient interface, where I can pass an
IDictionary<int,string^> to a method. int is some id and string^ is
some
value that is initialized to null by a caller.

The callee function needs to update the value based on the key.

in native code I'd have passed a std::map and do ((*it).second) = new
std::string("test");

I don't see a way to do it in managed code.

I obviously can't assign it to keyValuePair element as it's read only.
And I can't even do this:

for each ( KeyValuePair<int, string^>^ iter in testCollection )
{
testCollection[iter->key] = "test"
}

as on the second pass of the loop, the code above will throw since the
colleciton is modified.

You could file a bug against this, as it's improper behavior. Iterators
should only be invalidated when the morphology (shape) of the collection
changes, and updating a value doesn't do that. The version number
increment, which is causing that exception, should only be done when an item
is added or removed.

BTW KeyValuePair is a value type, so you shouldn't use ^ with it (doing so
isn't outright wrong, but it does slow you down because of boxing and
unboxing).
Thus, it looks like I need 2 passes, where I'd make a list of ids in
the first pass and on the second pass, I'd do something
like:
for ( int i = 0; i < ListIds.Count; ++i )
{
testCollection[listIds] = "test";
}

This code seems very poor. Is there really no way to update the
IDictionary<key,value> in 1 pass? I've got to be missing some
IEnumerator that would let me do that :)

Thanks in advance!
vcq
 
trying to design an effiecient interface, where I can pass an
IDictionary<int,string^> to a method. int is some id and string^ is
some
value that is initialized to null by a caller.
The callee function needs to update the value based on the key.
in native code I'd have passed a std::map and do ((*it).second) = new
std::string("test");
I don't see a way to do it in managed code.
I obviously can't assign it to keyValuePair element as it's read only.
And I can't even do this:
for each ( KeyValuePair<int, string^>^ iter in testCollection )
{
testCollection[iter->key] = "test"
}
as on the second pass of the loop, the code above will throw since the
colleciton is modified.

You could file a bug against this, as it's improper behavior. Iterators
should only be invalidated when the morphology (shape) of the collection
changes, and updating a value doesn't do that. The version number
increment, which is causing that exception, should only be done when an item
is added or removed.

BTW KeyValuePair is a value type, so you shouldn't use ^ with it (doing so
isn't outright wrong, but it does slow you down because of boxing and
unboxing).




Thus, it looks like I need 2 passes, where I'd make a list of ids in
the first pass and on the second pass, I'd do something
like:
for ( int i = 0; i < ListIds.Count; ++i )
{
testCollection[listIds] = "test";
}

This code seems very poor. Is there really no way to update the
IDictionary<key,value> in 1 pass? I've got to be missing some
IEnumerator that would let me do that :)
Thanks in advance!
vcq- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -


Thanks Ben! On the iteration ( 1-st question ) do you see a clean
workaround ( a different method of iterating through collection &
updating values )?
Also, thanks for pointing out the KeyValuePair issue!
 
Thanks Ben! On the iteration ( 1-st question ) do you see a clean
workaround ( a different method of iterating through collection &
updating values )?
Also, thanks for pointing out the KeyValuePair issue!

As I said, I think it is a bug in the BCL Dictionary<TKey,TValue>::Insert
helper function. There are two code paths, one for when the key already
exists, and one where it does not. The first of those should not increment
the private version variable which effectively kills all existing
enumerators.

If you submit a bug on Connect, post the link here. I will validate it and
vote for it.
 
As I said, I think it is a bug in the BCL Dictionary<TKey,TValue>::Insert
helper function. There are two code paths, one for when the key already
exists, and one where it does not. The first of those should not increment
the private version variable which effectively kills all existing
enumerators.

If you submit a bug on Connect, post the link here. I will validate it and
vote for it.

I got the bug part - I just refused to believe that MSFT gives us such
pleasant surprises -:) ( but after Debug::Assert( 0 ) showing up in
release mode - oh well... )

here's the link:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=298166
 
vcquestions said:
I got the bug part - I just refused to believe that MSFT gives us such
pleasant surprises -:) ( but after Debug::Assert( 0 ) showing up in
release mode - oh well... )

here's the link:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=298166

They appear to be hiding behind "It's not wrong, because we choose to define
the correct behavior as the way it works". I've added a bunch of counter
examples -- in C++/CLI -- to the bug report in the hope that somebody
realizes that there's value in having .NET collections act the same way
every other collection in the world does.
 
Thanks Ben! Frankly, I was not even familiar with Connect before this
issue come up. I always open bugs with MSFT by calling Dev. support
and shelling out $250 ( then they refund it eventually ). Obviously,
this enthusiasm could go only so far...:) We'll see if this could get
escalated.
 
Back
Top