views:

285

answers:

3

I'm a C++ expert, but not at all for C#. I created a Dictionary<string, STATS>, where STATS is a simple struct. Once I built the dictionary with initial string and STATS pairs, I want to modify the dictionary's STATS value. In C++, it's very clear:

Dictionary<string, STATS*> benchmarks;
Initialize it...

STATS* stats = benchmarks[item.Key];
// Touch stats directly

However, I tried like this in C#:

Dictionary<string, STATS> benchmarks = new Dictionary<string, STATS>();

// Initialize benchmarks with a bunch of STATS
foreach (var item in _data)
  benchmarks.Add(item.app_name, item);

foreach (KeyValuePair<string, STATS> item in benchmarks)
{
  // I want to modify STATS value inside of benchmarks dictionary.
  STATS stat_item = benchmarks[item.Key];
  ParseOutputFile("foo", ref stat_item);

  // But, not modified in benchmarks... stat_item is just a copy.
}

This is a really novice problem, but wasn't easy to find an answer.

EDIT: I also tried like the following:

  STATS stat_item = benchmarks[item.Key];
  ParseOutputFile(file_name, ref stat_item);
  benchmarks[item.Key] = stat_item;

However, I got the exception since such action invalidates Dictionary:

Unhandled Exception: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
  at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
  at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
  at helper.Program.Main(String[] args) in D:\dev\\helper\Program.cs:line 75
A: 

Try this:

STATS stat_item = benchmarks[item.Key]; 
ParseOutputFile("foo", ref stat_item); 
benchmarks[item.Key] = stat_item;

Note that even if STATS is a class, then updating the reference (as is implied by the ref keyword) will only update the local reference stat_item, not the value in the dictionary.

For example, the following code will modify the value in the dictionary if STATS is a class (but in this case the ref keyword is not needed and should be removed):

ParseOutputFile(string foo, ref STATS statItem)
{
    statItem.SomeProperty = ...
}

But the following will only affect the local variable and won't update the dictionary value, even if STATS is a class:

ParseOutputFile(string foo, ref STATS statItem)
{
    statItem = new STATS();
    ...
}
Joe
+6  A: 

If your STATS is indeed a struct, that means it's a value type, so where you do this:

STATS stat_item = benchmarks[item.Key];
ParseOutputFile("foo", ref stat_item);

Your stat_item is a copy of the value located at benchmarks[item.Key]. Thus when you pass it as a ref parameter to ParseOutputFile, only the copy is modified.

In the C++ code you posted, notice you would do what you're trying to accomplish here by using a pointer.

For .NET, the solution is simple: change STATS to a reference type (a class rather than struct). Then your local stat_item variable will be a reference to the same object referenced by the value of benchmarks[item.Key].

Dan Tao
Thanks, it just works well. I didn't know that such semantics.
minjang
+2  A: 

You should just change STATS to a class. Then you wouldn't need the ref keyword and the object would change.

The usual advice in C# is to use classes unless you are absolutely certain you need a struct.

BioBuckyBall
Thank you for pointing out!
minjang