Transferring Collections Inevitably

I am doing a lot of refactoring a bunch of code that used a bunch of multidimensional arrays that were constantly changing. I created a data object to replace a 2D array, and now I am passing a list of them.

I found something that bothers me a bit. Say I have a 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 starting points ( basePoints ) and a list of updated points ( newPoints ). But C # passes the list by reference, as with any object. This updates the basePoints in place, so now both basePoints and newPoints will have the same data.

At the moment, I try to be careful in creating a complete copy of the transferred List before I start with the data. Is this the only sensible way to make sure that changes to an object inside a function have no side effects outside the function? Is there something like passing an object using const ?

+6
source share
3 answers

In short: no.

C # has no concept of a const reference, as such. If you want to make an object immutable, you must explicitly encode it or use other "tricks".

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

So you really need to do what you want to return a deep copy or scroll, 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 = pX + foo, Y = pY + bar, Z = pZ + baz }); } 

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

UPDATE : or in .NET 2.0:

 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 fact is that there are many ways to consider something as immutable, but I would not try to implement the C ++ version of "const correctness" in C #, or you will lose your mind. Add it as needed when you want to avoid side effects, etc.

+3
source

You are probably looking for ReadOnlyCollection

Provides a base class for a general read-only collection.

Example:

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

Just draw attention to one fact: it provides a service to make a readonly (immutable) collection and not contain a type. I can get the object to crop this collection and modify it, and if the object is a reference type, these changes will also be reflected in the original collection.

If you want to have a readonly object, this gets a little more complicated (depends on the complexity of your object). The basic idea (presumably Servy, too) is to wrap your public object with open access by public members (therefore, for a consumer of this type it becomes unchanged).

Hope this helps.

+3
source

If you are using C # 4.5, I would recommend taking a look at Immutable Collections, which can be downloaded via the Nuget package ...

 PM> Install-Package Microsoft.Bcl.Immutable 

This blog post very well explains how to use them: http://blogs.msdn.com/b/dotnet/archive/2013/09/25/immutable-collections-ready-for-prime-time.aspx

To protect a private object, you must make all properties private. I like the .With..(..) template, which helps to create objects.

  public class NCPoint { public int X { get; private set; } public int Y { get; private set; } public int Z { get; private set; } public NCPoint(int x, int y, int z) { this.X = x; this.Y = y; this.Z = z; } public NCPoint WithX (int x) { return x == X ? this : new NCPoint(x, Y, Z); } public NCPoint WithY(int y) { return y == Y ? this : new NCPoint(X, y, Z); } public NCPoint WithZ(int z) { return z == Z ? this : new NCPoint(X, Y, z); } } 

You can use it as follows:

 var p = points[i]; var newPoint = p.WithX(pX + foo) .WithY(pY + bar) .WithZ(pZ + baz); 

I understand that this solution is not appreciated by all, as it requires a lot of coding. But I find it pretty elegant.

Finally, call the immutable list replacement method:

immutablePoints.Replace (p, newPoint);

0
source

Source: https://habr.com/ru/post/923151/


All Articles