-1

I have a code block similar to this:

List<Object> returnObjDataLine = new List<object>();

foreach (var tuple in dataPerTime)
{
    returnObjDataLine.Clear();
    returnObjDataLine.Add(tuple.Item1);
    foreach (var line in PlotLines)
    {
        if (tuple.Item2.Equals(line.Key))
        {
            returnObjDataLine.Add(tuple.Item3);
        }
        else
        {
            returnObjDataLine.Add(null);
        }
    }
    returnObjData.Add(returnObjDataLine);
}

However the Clear() method clears out the data already added to the returnObjData Dictionary, sort of. If there are 10,000 tuples, then after the loop runs the returnObjData will contain 10,000 instances of the very last data piece added (Clear() is not called after the last iteration).

If I modify the code to create a new List each iteration:

foreach (var tuple in dataPerTime)
{
    List<Object> returnObjDataLine = new List<object>();
    returnObjDataLine.Add(tuple.Item1);
    foreach (var line in PlotLines)
    {
        if (tuple.Item2.Equals(line.Key))
        {
            returnObjDataLine.Add(tuple.Item3);
        }
        else
        {
            returnObjDataLine.Add(null);
        }
    }
    returnObjData.Add(returnObjDataLine);
}

the loading loop works correctly but this seems very expensive as there can be 10s if not 100s of thousands of iterations required. Creating a new object every time seems to be inefficient.

What am I missing with Clear()? Is there some sort of "commit" that needs to be called first?

Omortis
  • 1,334
  • 19
  • 44
  • 7
    It's not clear what you expect to happen. In the first case, your `returnObjData` list contains the same `returnObjDataLine` reference lots of times. This is just how reference types work in .NET. What did you *expect* to happen and why? If you expected `returnObjData` to contain however many different lists, that's exactly what your second piece of code does. It's not clear how you expect to get lots of different objects without creating, well, *lots of separate objects*. If you could provide a [mcve] rather than snippets, and point out exactly what you expected, it would be easier to help. – Jon Skeet Jun 28 '18 at 19:30
  • Wow I searched for an answer to this without luck for quite a while. The answer linked above more or less answered my question. I was treating a reference like a value. Apparently the runtime "commits" the "values" of the list to the parent object (the last `Add()`) before creating the new List object with every iteration? – Omortis Jul 02 '18 at 12:42
  • No, there's no "commit" - it's just a matter of understanding how reference types work in .NET. – Jon Skeet Jul 02 '18 at 14:02

1 Answers1

-1

Looks like what you need to do is have a temporary list and a long term list... Something like this:

List<Object> longTermObjects = new List<object>();

foreach (var tuple in dataPerTime)
{
    List<Object> returnObjDataLine = new List<Object>();
    returnObjDataLine.Add(tuple.Item1);
    foreach (var line in PlotLines)
    {
        if (tuple.Item2.Equals(line.Key))
        {
            returnObjDataLine.Add(tuple.Item3);
        }
        else
        {
            returnObjDataLine.Add(null);
        }
    }
    longTermObjects.Add(returnObjDataLine);
}

This will give you a clean returnObjDataLine each iteration without removing the referenced items in longTermObjects.

Edit To Add Reference Type Information:

By default .NET will store 1 copy of an object into memory, and then "reference" that object anywhere you use it. Take the following example:

int A = 1;
int B = A;
Console.WriteLine($"A = {A}");
Console.WriteLine($"B = {B}");
A = 0;
Console.WriteLine($"A = {A}");
Console.WriteLine($"B = {B}");

Result:

A = 1
B = 1
A = 0
B = 0

Why you ask does B = 0 on the 4th line? Because B had a REFERENCE to A, it didn't contain the actual VALUE of A, so when A changed, B changed as well.

If I wanted B to contain only the value of A then I would need a way to create a new "value" instead of a reference. The way you do this varies for each type of object. One way and probably not the best would be something like:

int B = int.Parse(A.ToString());

Which would convert A to a string representing the value of A and then into a new int with the Parse. Now I have the value stored in B instead of just a reference.

If I wanted to do the same thing with a table of objects then I would have to do something like this:

List<MyObject> oldList = new List<MyObject>();
//Put some objects into oldList

List<MyObject> newList = oldList.Select(x => new MyObject(x.Param1, x.Param2)).ToList();

In this example I am basically taking each object in oldList and creating a new MyObject which is then put into newList.

  • 1
    He already has this in his question. He needs someone to explain reference types more clearly to him. – Hack Jun 28 '18 at 19:55
  • While I didn't give the minus one, for those that stumble across this and don't understand reference types, the example is wrong. int B = A; sets B to the value of A. Console.WriteLine($"B = {B}"); will write out 1. int is defaulted to a value type, not reference type. – Hack Jun 29 '18 at 10:59