6

私は、常にサイズ変更されていた多次元配列の束を使用していた大量のコードの大きなリファクタリングを行っています。2D 配列を置き換えるデータ オブジェクトを作成し、これらのリストを渡しています。

ちょっと気になるところも発見。次のようなコードがあるとします。

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;
}

アイデアは、元のポイントbasePointsのリスト ( ) と更新されたポイントのリスト( ) を保持することnewPointsです。ただし、C# は、他のオブジェクトと同様に、参照によってリストを渡します。これは適切に更新されるため、とのbasePoints両方に同じデータが含まれます。basePointsnewPoints

現時点ではList、データをいじる前に、渡されたものの完全なコピーを作成することに注意を払っています。関数内のオブジェクトへの変更が関数の外に副作用を及ぼさないことを確認する唯一の賢明な方法はありますか? でオブジェクトを渡すことに似たものはありますconstか?

4

3 に答える 3

3

おそらくReadOnlyCollectionを検索しています

一般的な読み取り専用コレクションの基本クラスを提供します。

例:

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

1つの事実に注意してください。これは、読み取り専用(不変)をコレクションにし、型を含まないようにするためのサービスを提供します。そのコレクションのオブジェクトを取得して変更できます。オブジェクトが参照型の場合、それらの変更は元のコレクションにも反映されます。

読み取り専用オブジェクトが必要な場合、これは少し注意が必要になります(オブジェクトの複雑さによって異なります)。基本的な考え方は(Servyによっても提案されています) 、読み取り専用のパブリックメンバーを使用して元のオブジェクトのラッパーを作成することです(したがって、そのタイプのコンシューマーの場合は不変になります)。

お役に立てれば。

于 2012-08-17T20:29:52.690 に答える
3

要するに:いいえ。

constC# には、参照自体の概念はありません。オブジェクトを不変にしたい場合は、そのように明示的にコーディングするか、他の「トリック」を利用する必要があります。

ReadOnlyColelctionコレクションをさまざまな方法 ( 、イテレータを返す、浅いコピーを返す) で不変にすることができますが、それはシーケンスを保護するだけで、内部に格納されているデータは保護しません。

したがって、必要なことを行うために本当に必要なのは、おそらく 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
        });
}

また、イテレータを返すことの利点 (単に aList<T>を として返すのとは対照的にIEnumerable<T>) は、元のコレクション型にキャストバックできないことです。

更新: または、.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;
}

重要なのは、何かを不変として扱う方法はたくさんあるということですが、C# で C++ スタイルの "const correctness" を実装しようとはしません。副作用などを避けたい場合など、必要に応じて実装してください。

于 2012-08-17T20:34:06.727 に答える
0

C# 4.5 を使用している場合は、Nuget パッケージからダウンロードできる Immutable Collections を参照することをお勧めします...

PM> Install-Package Microsoft.Bcl.Immutable

このブログ投稿は、それらの使用方法を非常によく説明しています: http://blogs.msdn.com/b/dotnet/archive/2013/09/25/immutable-collections-ready-for-prime-time.aspx

囲まれたオブジェクトを保護するには、すべてのプロパティを非公開にする必要があります。.With..(..) オブジェクトを構築するのに役立つパターンが好き

    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);
        }
    }

次のように使用できます。

var p = points[i];
var newPoint = p.WithX(p.X + foo)
                .WithY(p.Y + bar)
                .WithZ(p.Z + baz);

多くのコーディングが必要なため、このソリューションが万人に好まれるわけではないことは理解しています。しかし、私はそれが非常にエレガントだと思います。

最後に、不変リストの Replace メソッドを呼び出します。

immutablePoints.Replace (p, newPoint);

于 2014-03-17T15:56:07.610 に答える