0

I'm new to C#, and I created a struct (MyMeshFace) and initialized an array consisting of some 40.000 of those structs, like so:

MyMeshFace[] m_meshFaces = new MyMeshFace[mesh.Faces.Count]; // ~40.000 faces

I then populate the array with data in a for-loop, debugging it all with breakpoints so that I can see with my own eyes that field objects are instantiated with empty values (i.e. edge = new Line(); etc) and after this initialization, seeing also that values are properly assigned to fields and object fields (like Line.From = Point; & Line.To = AnotherPoint; etc).

Problem: But, when I run through this list of m_meshFaces a second time, this time calling a function embedded in the struct in order to make the struct update its internal data, then the objects (Lines) no longer has any values! The Line objects in the structs really are instantiated, and thus they're valid objects and so they don't throw exceptions when accessed, but their values (X,Y,X) are zeroed (although I could via breakpoints see that the Lines were originally assigned with values in the first loop when they were initialized). And I have no idea why this is happening.

The code is straightforward:

public struct MyMeshFace
{
    public Line edgeAB;
    public Line edgeBC;
    ...
    public int facesABC[];
    public Point3d[] pointsABC;
    ...
    public void Init(Mesh m, int index)
    {
        edgeAB = new Line();
        edgeBC = new Line();
        ...
    }
    public void SetFaceData(Mesh m)
    {
        // UPDATE 3: Face corner indexes (to points) from mesh:
        var face = m.Faces[faceIndex];
        facesABC[xA] = face.A;
        facesABC[xB] = face.B;

        pointsABC[xA] = m.Vertices[facesABC[0]];
        pointsABC[xB] = m.Vertices[facesABC[1]];
        ...
        edgeAB.From = pointsABC[xA];
        edgeAB.To = pointsABC[xB];
        ...
    }
    public bool DoSomething()
    {
        if (edgeAB.From.X == 0 && edgeAB.From.Y == 0 && edgeAB.From.Z == 0)
           return false;   /* Fail ! */
        // 
        // Execution never makes it here :(
        // 
        return true;
    }
        ...
}

Main method (Update 2: Added call to Init and SetFaceData)

public void Main()
{   
    MyMeshFace[] m_meshFaces = new MyMeshFace[m_mesh.Faces.Count];
    for (var f = 0; f < m_meshFaces.GetLength(0); f++)
    {
        var face = m_meshFaces[f];
        face.Init(m_mesh, f);
        face.SetFaceData(m_mesh);
    }

    for (var x = 0; x < m_meshFaces.GetLength(0); x++)
    {
        var face = m_meshFaces[x];
        if (face.DoSomething())
            Print("Did something!");  # <-- This never happens
    }
}

I tried reading on Microsofts pages about structs, but no clue there. What am I doing wrong?


Edit FIX: This code works, according to anwers below:

public void Main()
{   
    MyMeshFace[] m_meshFaces = new MyMeshFace[m_mesh.Faces.Count];
    for (var f = 0; f < m_meshFaces.GetLength(0); f++)
    {
        // var face = m_meshFaces[f];       // <-- No no!
        // face.Init(m_mesh, f);            // <-- No no!
        // face.SetFaceData(m_mesh);        // <-- No no!

        m_meshFaces[f].Init(m_mesh, f);     // <-- OK! 
        m_meshFaces[f].SetFaceData(m_mesh); // <-- OK
    }

    for (var x = 0; x < m_meshFaces.GetLength(0); x++)
    {
        if (m_meshFaces[x].DoSomething())
            Print("Did something!");  // OK!
    }
}

// Rolf

17
  • 4
    I gotta ask, any good reason MyMeshFace is a struct and not a class? Structs are generally good for things like a 2D or 3D point. If you need something more elaborate, classes are preferred. Read this learn.microsoft.com/en-us/dotnet/csharp/programming-guide/… Commented Oct 15, 2017 at 2:49
  • 3
    We need a minimal reproducible example. Where is m_meshFaces declared and populated? Where do your values come from? Commented Oct 15, 2017 at 2:49
  • 3
    Mutable structs are a particularly bad idea. I'm guessing Line and Point3d are also structs? I think you need to carefully read this question and its answers. I strongly suspect your problem is something to do with unexpected value-type semantics. Commented Oct 15, 2017 at 3:01
  • 1
    I'm sure someone will be able to figure this out if you can pare your code down to a minimal reproducible example that we can paste into Visual Studio. Otherwise, it's very hard to see what's going on. Commented Oct 15, 2017 at 3:10
  • 3
    @RIL, I think the bug may be in your first for loop in the main. I think the line var face = m_meshFaces[f]; creates a copy of the struct that you then initialize. Instead of creating the separate variable face, try doing this: m_meshFaces[f].Init(m_mesh, f); and m_meshFaces[f].SetFaceData(m_mesh); Does that help? Commented Oct 15, 2017 at 3:32

2 Answers 2

5

When you do var face = m_meshFaces[f]; in your code (added comments to hightlight):

public void Main()
{   
    MyMeshFace[] m_meshFaces = new MyMeshFace[m_mesh.Faces.Count];
    for (var f = 0; f < m_faceInfo.GetLength(0); f++)
    {
        var face = m_meshFaces[f]; // <---
        face.Init(m_mesh, f);
        face.SetFaceData(m_mesh);
    }

    for (var x = 0; x < m_meshFaces.GetLength(0); x++)
    {
        var face = m_meshFaces[x]; // <---
        if (face.DoSomething())
            Print("Did something!");  # <-- This never happens
    }
}

You get a COPY of the struct.

Then you modify the copy, and never replace the original with the copy.

On the second loop, you take a A NEW COPY of the struct.


As per solutions, you may replace the original with the copy by doing m_meshFaces[f] = face; Or you may try working with the struct in the array directly:

m_meshFaces[f].Init(m_mesh, f);
m_meshFaces[f].SetFaceData(m_mesh);

This is explained on Microsoft Documentation:

Structs are copied on assignment. When a struct is assigned to a new variable, all the data is copied, and any modification to the new copy does not change the data for the original copy. This is important to remember when working with collections of value types such as Dictionary.

-- Structs (C# Programming Guide)

Sign up to request clarification or add additional context in comments.

3 Comments

Yup, this is what I meant by "unexpected value type semantics"
Ah, thanks for this. I was totally lost on this one! I'll try this in a minute.
For not only linking to the official docs for structs, but quoting the relevant part of the text, +1. I wish it was done more often here on SO. (And I'm guilty of not doing so myself, as often as I could/should)
1

There seems to be a misconception in your part about how structs are handled when assigned to a variable. structs are value types, so when you assign var something = struct, the variable doesn't receive a reference to the struct's object, unlike a class (reference type); instead, it receives a copy of the value that the struct represents.

With that in mind, assignments like var face = m_mesh.Faces[f]; will not work the way you seem to be expecting them to. Your handling of face on the first for-loop will not affect the original m_mesh.Faces[f], unless you reassign face back to m_mesh.Faces[f] after it's modified.


Try the following code:

public void Main()
{   
    MyMeshFace[] m_meshFaces = new MyMeshFace[m_mesh.Faces.Count];
    for (var f = 0; f < m_faceInfo.GetLength(0); f++)
    {
        var face = m_mesh.Faces[f];
        face.Init(m_mesh, f);
        face.SetFaceData(m_mesh);
        m_mesh.Faces[f] = face; //<<<-----
    }

    for (var x = 0; x < m_meshFaces.GetLength(0); x++)
    {
        var face = m_meshFaces[x];
        if (face.DoSomething())
            Print("Did something!");
        m_meshFaces[x] = face; //<<<-----
    }
}

Update: Corrections to a typo in the OP made this section obsolete. It will be kept for future reference only.

Additionally: You also seem to have a misconception about array initialization and/or structs' default value.

Value types, incl. structs, cannot be null; they are always initialized to a default value. Since you are not populating the m_meshFaces array with any custom data, your command MyMeshFace[] m_meshFaces = new MyMeshFace[m_mesh.Faces.Count]; makes the array be only populated by default MyMeshFace structs. This is because array initialization new array[amount] only reserves the memory slots for a certain quantity (amount) of objects. It's not copying the original m_mesh.Faces collection; it's creating new Face objects entirely, all with default internal values.

The important thing about this is that if Line is also a struct (also initialized to defaults, which are probably 0), then...

if (edgeAB.From.X == 0 && edgeAB.From.Y == 0 && edgeAB.From.Z == 0)
           return false;   /* Fail ! */

...will always fail; as the From values are all always default, which is probably 0 on all, X & Y & Z.

5 Comments

Yes, this is clear from the prevoius answer. I also fixed a typo ("m_mesh.Faces" -> "m_meshFaces", and "m_faceInfo" (wrong name) -> "m_meshFaces", but that had nothing with the core problem. Thanks anyway all of you for your help. I knew I ha a problem with structs (hence my description focussing on that) but apart from that I was totally lost. Thanks!
@RIL Added a second part that may be important to your problem. Please check it out.
There's no problems with the structs, nor with the lines. I do initialize them as "zero" values yes, but I do assign values to them in the SetFaceData() methods, and now it all works just fine. The problem was all about copying the structs in the loop.
@RIL Ok then. It's just that before the edit, as far as we knew, you were dealing with two different arrays (m_mesh.Faces VS m_meshFaces), so in that scenario the second loop would always have defaults. =)
I don't know why, but initially I had m_meshFaces (no dot) in both cases, then I thought I had a typo there and modified it. But now I modified it back again. The confusing comes from the fact that when retrievening faces from the real mesh, the source mesh, it looks exactly like that: "m_mesh.Faces[index]". Arghh. It can get messy when trying to simplify code without compiling it :) I also had the wrong variable name (m_faceInfo). But was fixed as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.