Click here to Skip to main content
15,885,366 members
Please Sign up or sign in to vote.
1.00/5 (3 votes)
See more:
//riddle me this - run it and after you get over your shock - try to explain it. I've asked MSDN and they surrendered.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ArrayIt
    {
    class arrayit
        {
            public int i;
            public int j;

            public struct ytimeframe
                {
                public int ytime;
                }

            public struct xevents
                {
                public int id;
                public ytimeframe[] xy_array;
                }

            public xevents[] xevents_array;

        static void Main(string[] args)
            {

            new arrayit().Mainline();

            }
        void Mainline()
            {

            i = 0;
            j = 0;

            xevents_array = new xevents[5];    //define

            for (j = 0; j < 5; j++)
                xevents_array[j].xy_array = new ytimeframe[5];    //define

            for (j = 0; j < 5; j++)
                xevents_array[0].xy_array[j].ytime = j;    //define

            xevents_array[0].id = 0;    //initialize

            for (i = 1; i < 5; i++)    //initialize
                {
                xevents_array[i] = xevents_array[0];
                xevents_array[i].id = i;
                }

            Console.WriteLine("Before ");
            for (i = 0; i < 5; i++)
                for (j = 0; j < 5; j++)
                    Console.WriteLine(xevents_array[i].id + " " + xevents_array[i].xy_array[j].ytime);

            xevents_array[0].id = 454;
            xevents_array[0].xy_array[0].ytime = 454;     // notice the hard coded indexes

            Console.WriteLine("After ");        //please explain the result
            for (i = 0; i < 5; i++)
                for (j = 0; j < 5; j++)
                    Console.WriteLine(xevents_array[i].id + " " + xevents_array[i].xy_array[j].ytime);

            while (Console.Read() != 'q');

            }
        }
    }



Console-Output:
C#
Before
0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
2 4
3 0
3 1
3 2
3 3
3 4
4 0
4 1
4 2
4 3
4 4
After
454 454
454 1
454 2
454 3
454 4
1 454
1 1
1 2
1 3
1 4
2 454
2 1
2 2
2 3
2 4
3 454
3 1
3 2
3 3
3 4
4 454
4 1
4 2
4 3
4 4
Posted
Updated 18-Mar-15 4:52am
v3
Comments
Thomas Daniels 18-Mar-15 10:29am    
Can you please explain what you expect, and what doesn't work? Without knowing this, it's hard to know what to look for.
[no name] 18-Mar-15 10:57am    
Certainly
This is a very simple common structure in the real world - and don't suggest using lists because it gets more complicated.
Clearly the compiler is ok with declare of the structures and the structure containing an array - being itself an array. the complier has no issues defining the size of each X & Y . The initialization functions perfectly - as proved in the "before" loop write. Here is where it gets interesting. The ++. The inclement "should" increment the first Y of the first X. Instead it set the first Y of each X. Initially in my "real" program (converted from C++) my numbers were all way off base - so I started small and set everything to 0 and then did a simple increment ++. To my shock, every first Y of all the X were incremented by 1! So then I tried a = a + 1, same thing happened . Then I tried a = 1. Same thing. Each first Y of each X was set. So in desperation I hard coded both indexes to 0 - same thing. Now if you take this code and compile it as C++ - it will work perfectly. Setting the 0,0 element to any value will set only one value - not the first Y of all the X elements. In English , you can see that only the 0,0 element was set - yet - in the after - you can see that element 1,0 - 2,0 - 3,0 & 4,0 were also set. This is bewildering. Note: I cannot use List because the structures are actually more complex and as I add elements - I sort the array - so that I can perform a binary search to point to where I can do the ++. Like I said, within .Net in C++ this is perfectly Kosher.
Sergey Alexandrovich Kryukov 18-Mar-15 11:18am    
How about using the debugger? All the code looks trivial.
As to "comparison" with C++, it sounds weird; why would you, generally, expect the same behavior from some pretty much unrelated language?
—SA

What shock? It does what I would expect.e
Your xevents is a struct - which means that it's a valuetype.
It contains an array of ytimeframe ojbects, which because it's an array is a reference type, regardless of the type of the objects it is an array of.

So when you do this:
C#
for (i = 1; i < 5; i++)    //initialize
    {
    xevents_array[i] = xevents_array[0];
    xevents_array[i].id = i;
    }
You use a valuetype assignment to copy the individual members of the xecevent_array - which copies the reference to the same array of ytimeframe objects into each and every xevents instance.
So when you modify any element of the xy_array you modify the same element in all instances in your xevents_array.
 
Share this answer
 
The problem is with this line:
C#
xevents_array[i] = xevents_array[0];

You're copying the value of the xevents structure at position 0 to each element of the array. However, this is a shallow copy. Since the struct contains a reference type (the xy_array array), each copy of the struct will be pointing to the same array instance.

When you change a value in the array instance for the first item, all of the other items are updated because they're looking at the same array instance.

A simpler demonstration of the issue:
C#
static void Main()
{
    Foo x = new Foo();
    x.Bar = new int[1];
    x.Bar[0] = 1;

    Foo y = x;
    y.Bar[0] = 42;

    Console.WriteLine(x.Bar[0]); // Output is 42
}

public struct Foo
{
    public int[] Bar;
}

To resolve the issue, you'll need to clone the array:
C#
// xevents_array[i] = xevents_array[0];
xevents_array[i].xy_array = (ytimeframe[])xevents_array[0].xy_array.Clone();
 
Share this answer
 
edit: I just realized I only explained why it doesn't work like you expect in C#, I can't tell why it's different in C++ /edit

You replace the references to the individual xy_arrays in xevents_array 1-4 with the reference to the xy_array of xevents_array 0 here:
C#
for (i = 1; i < 5; i++)
{
    xevents_array[i] = xevents_array[0];  // <- here
    xevents_array[i].id = i;
}

So already before the first WriteLine-Loop you're seeing the values from one and the same xy_array and consequentially you change all the zeroes the one zero in the 0-index to 454 here:
C#
xevents_array[0].xy_array[0].ytime = 454;
 
Share this answer
 
C#
i = 0;
j = 0;

// create 5 xevents elements
xevents_array = new xevents[5];

// xevents_array[0] is at address 1000
// xevents_array[1] is at address 1032
// xevents_array[2] is at address 1064
// xevents_array[3] is at address 1096
// xevents_array[4] is at address 1128

for (j = 0; j < 5; j++)
    xevents_array[j].xy_array = new ytimeframe[5];

// xevents_array[0] is at address 1000
//     xevents_array[0].xy_array references an array at 2000
// xevents_array[1] is at address 1032
//     xevents_array[1].xy_array references an array at 2016
// etc

for (j = 0; j < 5; j++)
    xevents_array[0].xy_array[j].ytime = j;

// xevents_array[0] is at address 1000
//     xevents_array[0].xy_array references an array at 2000
//         at 2000 is an array {0, 1, 2, 3, 4}
// xevents_array[1] is at address 1032
//     xevents_array[1].xy_array references an array at 2016
//         at 2016 is an array {0, 1, 2, 3, 4}

xevents_array[0].id = 0;

for (i = 1; i < 5; i++)
{
    xevents_array[i] = xevents_array[0]; // make every member from 1 to 5 reference member 0
    // the important thing to note here is that you are not getting a *copy* of what is at xevents_array[0]
    // you are setting each array to reference the same element
    xevents_array[i].id = i;
}

// now your array looks like this

// xevents_array[0] is at address 1000
//     xevents_array[0].xy_array references an array at 2000
//         at 2000 is an array {0, 1, 2, 3, 4}
// xevents_array[1] is at address 1000
//     xevents_array[1].xy_array references an array at 2000
//         at 2000 is an array {0, 1, 2, 3, 4}
// etc

Console.WriteLine("Before ");
for (i = 0; i < 5; i++)
    for (j = 0; j < 5; j++)
        Console.WriteLine(xevents_array[i].id + " " + xevents_array[i].xy_array[j].ytime);

xevents_array[0].id = 454;
xevents_array[0].xy_array[0].ytime = 454;     // notice the hard coded indexes

// xevents_array[0] is at address 1000
//     xevents_array[0].xy_array references an array at 2000
//         at 2000 is an array {454, 1, 2, 3, 4}
// xevents_array[1] is at address 1000
//     xevents_array[1].xy_array references an array at 2000
//         at 2000 is an array {454, 1, 2, 3, 4}
// etc

Console.WriteLine("After ");        //please explain the result
for (i = 0; i < 5; i++)
    for (j = 0; j < 5; j++)
        Console.WriteLine(xevents_array[i].id + " " + xevents_array[i].xy_array[j].ytime);

while (Console.Read() != 'q') ;
 
Share this answer
 
Greetings everybody - never mind my programming style - here is the same logic in .Net C++ - check out the results - they speak for themselves

CSS
// ArrayItcpp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "iostream"
#include "ostream"

int _tmain(int argc, _TCHAR* argv[])
{

    int i = 0;
    int j = 0;
    char str[256];

    struct ytimeframe
    {
        int ytime;
    } cppisdumb;
// declare and define array size
    struct xevents
    {
        int id;
        ytimeframe xy_array[5];
    } xevents_array[5];

// initialize
    for (j = 0; j < 5; j++)
        xevents_array[0].xy_array[j].ytime = j;

    xevents_array[0].id = 0;

    for (i = 1; i < 5; i++)
    {
        xevents_array[i] = xevents_array[0];
        xevents_array[i].id = i;
    }
//display the contents berfore the "increment"
    std::cout << "Before " << std::endl;

    for (i = 0; i < 5; i++)
        for (j = 0; j < 5; j++)
            std::cout << xevents_array[i].id << " " << xevents_array[i].xy_array[j].ytime << std::endl;
// "increment"
    xevents_array[0].id = 454;
    xevents_array[0].xy_array[0].ytime = 454;

//display the contents after the "increment"
    std::cout << "After " << std::endl;

    for (i = 0; i < 5; i++)
            for (j = 0; j < 5; j++)
                std::cout << xevents_array[i].id << " " << xevents_array[i].xy_array[j].ytime << std::endl;

    std::cin.get(str, 256);

    return 0;
}
Before
0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
2 4
3 0
3 1
3 2
3 3
3 4
4 0
4 1
4 2
4 3
4 4
After
454 454
454 1
454 2
454 3
454 4
1 0   < - this is as expected 
1 1
1 2
1 3
1 4
2 0   < - this is as expected 

2 1
2 2
2 3
2 4
3 0   < - this is as expected 

3 1
3 2
3 3
3 4
4 0   < - this is as expected 

4 1
4 2
4 3
4 4
 
Share this answer
 
Comments
Richard Deeming 18-Mar-15 15:18pm    
This is not an answer.
CSS
ABC#

The purpose of this document is to point out what I firmly believe is a bug in the .Net Compiler for C#. This bug is very well hidden, but easy to demonstrate in testing.

Before I get too far along, a little background. I have been coding, professionally since 1982. I have a very deep understanding of programming concepts and I do not need to hear about what variables need to be public etc. I do not need a programming lesson. I would like to see non trivial examples in books and the internet, but I guess that void will be filled by me at some time in the future.

Here is the task: count the number of times event X occurs throughout Y time. The reality is, there are millions of events, thousands of event types, covering over a decade and we’d like to see the percentage of change overtime. So in order to capture the events and the times, we load then into the first available slot, and if we add a new value, we sort the array so that we can perform a binary search prior to the next increment. Executing millions of "For loops" in two dimensions makes the computer squirm.

Simple task. Simple solution. Let’s look at the very basic parts. We need a two dimensional array with a counter to increment.

So let’s be like everybody else and create a trivial example. Both in .Net. One in C#, the other in C++. We’ll compare and contrast the code and the results, line by line, concept by concept.  Prepare to be baffled.

Part I: Define / Declare variables

In C#

public int i;
public int j;

     public struct ytimeframe
     {
            public int ytime;
     }

     public struct xevents
     {
            public int id;
           public ytimeframe[] xy_array;
     }

     public xevents[] xevents_array;

    In English, variables i and j are numbers to be used to reference the X and Y axis of the array. The Y axis is a number  in a real world there would be a Month and a Year. The X axis is the id of the event, we do not know what they are up front  or how many we could have but we make a reasonable estimate and test the limit in real life. For this trivial example we’ll have 5 events and 5 time periods. We can see that in C# we declare that there will be an array containing an array  but we cannot size it yet.

In C++

int i = 0;
    int j = 0;

    struct ytimeframe
    {
        int ytime;
    } cppisdumb;

    struct xevents
    {
        int id;
        ytimeframe xy_array[5];
    } xevents_array[5];

The difference in C++ is that the size of the arrays is set up front. I say cppisdumb because you cannot simply define a struct and not immediately declare something of its type, so I did that. I know that a struc with only one item is silly  but  let’s put that to the side for now.


Part II: Initialize the size of the array

In C#

xevents_array = new xevents[5];

     for (j = 0; j < 5; j++)
         xevents_array[j].xy_array = new ytimeframe[5];


In C# here is where we need to define the size of the arrays. This is not for the timid, as each array Y within X must be sized too.

In C++ - this is already done above  the size is within the declare

Part III: Initialize the array values

    In C#

for (j = 0; j < 5; j++)
                xevents_array[0].xy_array[j].ytime = j;

xevents_array[0].id = 0;

            for (i = 1; i < 5; i++)
                {
                xevents_array[i] = xevents_array[0];
                xevents_array[i].id = i;
                }

    Now for the uninitiated, no pun intended, this is an advanced concept. We set the initial value of each first Y element. Then we set the initial value of the first X elements. Then we set the other X elements to the same value as the first X. Build a real array and then do the math.

In C++

for (j = 0; j < 5; j++)
        xevents_array[0].xy_array[j].ytime = j;

    xevents_array[0].id = 0;

    for (i = 1; i < 5; i++)
    {
        xevents_array[i] = xevents_array[0];
        xevents_array[i].id = i;
    }

Notice that, for sanity, I did not set everything to zero, but instead set everything to index of the axis. Also note that the code is exactly the same in C# and C++.

Part IV: display the contents of the array  before incrementing (or setting) any values

In C#

Console.WriteLine("Before ");

for (i = 0; i < 5; i++)
            for (j = 0; j < 5; j++)
Console.WriteLine(xevents_array[i].id
+ " "
+ xevents_array[i].xy_array[j].ytime);

In C++

std::cout << "Before "
     << std::endl;

    for (i = 0; i < 5; i++)
        for (j = 0; j < 5; j++)
            std::cout << xevents_array[i].id
<< " "
<< xevents_array[i].xy_array[j].ytime << std::endl;

    There is nothing strange here other than the syntax. The output is the same.

Before
0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
2 4
3 0
3 1
3 2
3 3
3 4
4 0
4 1
4 2
4 3
4 4

Part V: increment the counter

In C#

xevents_array[0].id = 454;                          xevents_array[0].xy_array[0].ytime = 454;

In C++

xevents_array[0].id = 454;
    xevents_array[0].xy_array[0].ytime = 454;

    Here is where I get tongue in cheek. This is because when I noticed the issue I was doing the normal "++" thing. Until I noticed that it was acting poorly. Notice that the syntax  again is exactly the same. Because I know how screwy the results were, I changed the instruction to a simple assignment of a specific index value.  The real code is an increment of a counter.

Part VI: display the contents of the array  after incrementing

In C#

Console.WriteLine("After ");

     for (i = 0; i < 5; i++)
            for (j = 0; j < 5; j++)
            Console.WriteLine(xevents_array[i].id
 + " "
 + xevents_array[i].xy_array[j].ytime);

After
454 454
454 1
454 2
454 3
454 4
1 454
1 1
1 2
1 3
1 4
2 454
2 1
2 2
2 3
2 4
3 454
3 1
3 2
3 3
3 4
4 454
4 1
4 2
4 3
4 4

Even though the set command had both indexes hard coded- each first Y of each X was set to 454!

In C++

std::cout << "After "
    << std::endl;

    for (i = 0; i < 5; i++)
        for (j = 0; j < 5; j++)
            std::cout << xevents_array[i].id
    << " "
    << xevents_array[i].xy_array[j].ytime
    << std::endl;

After
454 454
454 1
454 2
454 3
454 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
2 4
3 0
3 1
3 2
3 3
3 4
4 0
4 1
4 2
4 3
4 4

 Part VII: a discussion about what the compiler did

    In C#

        We got what we wanted in the definition and the declaration of the size of the array. And we even got the initialization exactly as we expected.

        Where C# went "south" is in the assign. Any and all attempts to get 0,0 to be set and not anything else have failed.

    In C++

        We got exactly what we wanted. We got what we would get in Fortran, Pascal, Cobol, VB  all languages I have had the pleasure of solving this task in.

    My conclusion is that the .Net C# Compiler has a booboo.
 
Share this answer
 
Comments
Richard Deeming 18-Mar-15 15:19pm    
This is not an answer, and the behaviour you are seeing is not a bug.
[no name] 18-Mar-15 15:33pm    
Hi Richard
How can it be that the exact same code - giving different results is not a bug?
if you know - please enlighten me
post me the code in C# that performs as C++ does.
I've tried many ways
and please do not send me to the LIST type
thanks
Richard Deeming 18-Mar-15 15:39pm    
It is NOT the exact same code. Whilst C# and C++ share a very similar syntax, they are completely different languages.

There are four answers to this question explaining why the C# code works as it does. If you want to be enlightened, then try reading them instead of dismissing them!

My answer includes an example of how to change your C# code to make it work as you expect. I'm not going to repeat it here. Read my answer and make the suggested change to your C# code, and it will give you the same output as the C++ code.

Nobody here has suggested using the List<T> type, so I'm not sure where you're getting that idea from.
phil.o 18-Mar-15 21:52pm    
Please stop posting non-answers that just repeat the content of your original question. If you want to give more details, then you can improve the question. I flagged your first non-answer as "Not an answer", but I flagged the second one as abusive, as I would do with all subsequent ones. Please respect the rules of the forum, they are here for good reasons.
[no name] 19-Mar-15 9:29am    
Hi Phil. I understand the frustration. The point that I'm failing to get across is simple. The initialization logic is working perfectly, the proof is the "before". This is proof that the declare and definition is good. What is not good is the assignment. There is no need to explain "cloning". You and this forum can drop this. I will not be coding in C# henceforth. If I encounter anymore .Net issues - I'll have to start considering other options like WebSphere.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900