不可挽回地传递收藏品

KCh*_*oux 6 c#

我正在做一大堆代码的重构,这些代码过去常常使用一堆不断调整大小的多维数组.我创建了一个数据对象来替换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;
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是保留原始点(basePoints)的列表和更新点的列表(newPoints).但是C#通过引用传递列表,就像任何对象一样.这将更新basePoints到位,所以现在都basePointsnewPoints将具有相同的数据.

目前,我正在努力制作一份传入的完整副本,List然后才能查看数据.这是确保对函数内对象的更改在函数外部没有副作用的唯一合理方法吗?是否有类似于传递对象的东西const

Jam*_*are 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
        });
}
Run Code Online (Sandbox Code Playgroud)

此外,返回迭代器(而不是仅将 aList<T>作为 anIEnumerable<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;
}
Run Code Online (Sandbox Code Playgroud)

关键是,有很多方法可以将某些东西视为不可变,但我不会尝试在 C# 中实现 C++ 风格的“常量正确性”,否则你会发疯的。当您想避免副作用等时,请根据需要实施它。