Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Collections Immutably

Tags:

c#

I'm doing a big refactor of a pile of code that used to use a bunch of multidimensional arrays that were constantly getting resized. I created a data object to replace the 2D array, and I'm now passing a list of these around.

I discovered something that worries me a little though. Let's say I have some code that looks like this:

List<NCPoint> basePoints = new List<NCPoint>();

// ... snip populating basePoints with starting data

List<NCPoint> newPoints = TransformPoints(basePoints, 1, 2, 3);

public List<NCPoint> TransformPoints(List<NCPoint> points, int foo, int bar, int baz){
    foreach(NCPoint p in points){
        points.X += foo
        points.Y += bar
        points.Z += baz
    }

    return points;
}

The idea is to keep a list of the original points (basePoints) and a list of the updated points (newPoints). But C# passes the list by reference, as with any object. This updates basePoints in place, so now both basePoints and newPoints will have the same data.

At the moment, I'm trying to be careful about making a full copy of the passed-in List before I muck with the data. Is that the only sensible way to make sure changes to an object within a function don't have side effects outside the function? Is there anything akin to passing an object with const?

like image 423
KChaloux Avatar asked Aug 17 '12 20:08

KChaloux


People also ask

Are collections immutable?

The collections returned by the convenience factory methods added in JDK 9 are conventionally immutable. Any attempt to add, set, or remove elements from these collections causes an UnsupportedOperationException to be thrown.

How do you make a collection object immutable?

In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.

Is a collection of objects which ordered and immutable?

A set is an ordered collection of objects.

Are asList arrays immutable?

util. Arrays. asList() , the list is immutable.


2 Answers

In short: no.

C# does not have the concept of a const reference, per se. If you want to make an object immutable, you must code it explicitly so or take advantage of other "tricks".

You can make your collection immutable in many ways (ReadOnlyColelction, return an iterator, return a shallow copy) but that only protects the sequence, not the data stored inside.

Thus, what you'd really need to do what you want is to return a deep copy or projection, possibly using LINQ:

public IEnumerable<NCPoint> TransformPoints(List<NCPoint> points, int foo, int bar, int baz)
{
    // returning an iterator over the sequence so original list won't be changed
    // and creating new NCPoint using old NCPoint + modifications so old points
    // aren't altered.
    return points.Select(p => new NCPoint
        { 
           X = p.X + foo,
           Y = p.Y + bar,
           Z = p.Z + baz
        });
}

Also, the beauty of returning an iterator (as opposed to just returning a List<T> as an IEnumerable<T>, etc.) is that it can't be cast back to the original collection type.

UPDATE: Or, in .NET 2.0 parlance:

public IEnumerable<NCPoint> TransformPoints(List<NCPoint> points, int foo, int bar, int baz)
{
    // returning an iterator over the sequence so original list won't be changed
    // and creating new NCPoint using old NCPoint + modifications so old points
    // aren't altered.
    NCPoint[] result = new NCPoint[points.Count];

    for (int i=0; i<points.Count; ++i)
    { 
        // if you have a "copy constructor", can use it here.
        result[i] = new NCPoint();
        result[i].X = points[i].X + foo;
        result[i].Y = points[i].Y + bar;
        result[i].Z = points[i].Z + baz;
    }

    return result;
}

The point is, there are many ways to treat something as immutable, but I wouldn't try to implement C++-style "const correctness" in C# or you will go mad. Implement it as needed when you want to avoid side-effects, etc.

like image 156
James Michael Hare Avatar answered Oct 17 '22 16:10

James Michael Hare


You probabbly searching for ReadOnlyCollection

Provides the base class for a generic read-only collection.

Example:

public IEnumerable<..> GetReadonlyCollection(List<...> originalList) 
{
  return new ReadOnlyCollection<string>(originalList);
}

Just invite attention on one fact: that this is provides service for making readonly (immutable) a collection and not containing type. I can get an object frim that collection and change it, and if the object is reference type, those changes would have their reflection in the original collection too.

If you want to have readonly object, this becomes a little bit more tricky (depends how complex your object is). The basic idea is (suggested by Servy too) is making a wrapper over your original object with readonly public members (so for consumer of that type it becomes immutable).

Hope this helps.

like image 28
Tigran Avatar answered Oct 17 '22 16:10

Tigran