0

I have a Point struct:

public struct Point
{
    public Point(double x, double y, double z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public double X;
    public double Y;
    public double Z;
}

then in the following code, I am creating an array of points and then dividing each points by a value 2

//Create a test array of Points
Point[] testPointArray = new Point[2];
testPointArray[0] = new Point(2, 4, 8);
testPointArray[1] = new Point(6, 12, 24);

//Divide by 2
testPointArray = testPointArray.Select(point => point = new Point(point.X / 2, point.Y / 2, point.Z / 2)).ToArray();

Question: In the above code, I am using the new operator to replace each point with a newly created point. How can I directly make changes in the existing point (i.e. avoid creating new points)? I tried to do the following code, but the syntax is wrong:

testPointArray = testPointArray.Select(point =>
{
    point.X = point.X / 2;
    point.Y = point.Y / 2;
    point.Z = point.Z / 2;
}).ToArray();
skm
  • 5,015
  • 8
  • 43
  • 104
  • 2
    You should create new instances, structs are value types and they should be immutable. – jalsh May 20 '22 at 10:01
  • if you used class instead of struct and its inside a list you can try `ForEach`, see [example](https://stackoverflow.com/a/200584/4648586). – Bagus Tesa May 20 '22 at 10:03
  • @jalsh interesting point, why? – orhtej2 May 20 '22 at 10:06
  • 1
    value types are copied when passed as arguments, hence any modification happening on them will happen within the scope of that function. This can cause a lot of confusion\errors. Thus it's recommended that you keep your structs immutable. [See this answer](https://stackoverflow.com/a/3751920/4259962) – jalsh May 20 '22 at 10:08
  • IMO (bring pitchforks and torches!) LINQ was meant as a functional extension (see [this](https://stackoverflow.com/questions/1418106/how-much-is-there-to-linq) and [this](https://stackoverflow.com/questions/101265/why-is-there-no-foreach-extension-method-on-ienumerable)). I would use a plain old `for` loop or, given you have 2 points, just inline the calls and skip looping altogether. – orhtej2 May 20 '22 at 10:18

1 Answers1

3
testPointArray = testPointArray.Select(point =>
{
    point.X /= 2;
    point.Y /= 2;
    point.Z /= 2;
    return point;
}).ToArray();

In your example your lambda returns nothing - adding return point returns the mutated point for your select.

I agree with jalsh's comment that you should create new points, something like:

testPointArray = testPointArray.Select(point => new Point(point.X / 2, point.Y / 2, point.Z / 2))
            .ToArray();
TVOHM
  • 2,740
  • 1
  • 19
  • 29
  • good answer, fyi for OP, this is still creating new points, you're just not initializing it yourself – jalsh May 20 '22 at 10:06
  • In both cases, you are still creating a new point. As, Jalsh mentioned, the Structs are immutable so, I should either use class to avoid creating new points OR else I must create a new point. – skm May 20 '22 at 10:13
  • One way to see this happening is by running `ForEach` instead of `Select` try: `Array.ForEach(testPointArray, point =>{point.X = point.X / 2; point.Y = point.Y / 2; point.Z = point.Z / 2;});` this will return the same (old) values although you have modified your fields – jalsh May 20 '22 at 10:17
  • @jalsh will that work if you're not pasing `point` as `ref point`? Doesn't seem to: https://ideone.com/FCSuPT – orhtej2 May 20 '22 at 10:24
  • @orhtej2 yes, it won't... that was my point (pun not intended). this shows that the function is operating on a COPY of Point – jalsh May 20 '22 at 10:25
  • @jalsh makes a lot of sense, I misread your comment. – orhtej2 May 20 '22 at 10:28