我应该重用作为参数传递的集合吗?

时间:2011-02-18 12:52:41

标签: c# .net reference parameter-passing

昨天我花了一些时间试图找到一个bug。长话短说,终于我意识到这是因为这个构造函数:

public Triangle(List<Vertex> vertices) {
    this._values = vertices;
}

我尝试使用值列表初始化对象,对象只是引用了我的对象而不是从列表中获取值。如果我不放弃我作为参数传递的列表并稍后使用它来做其他事情,比如用相同的值初始化其他东西,或者如果我决定清除它并填充新的值,我显然会破坏我的{的状态{1}}对象不知道它。

我的第一反应是在构造函数中“修复bug”,但后来我开始思考它是否真的应该如此。涵盖这类事情的良好做法是什么?一般来说,我应该如何看待采用值列表的构造函数/ init方法?他们应该完好无损吗?我是否允许重复使用列表,并在导致错误时出现错误?

我的意思是,我显然可以这样做:

Triangle

但不应该由var triangle = new Triangle(new List<Vertex>(vertices)); 类的创建者完成吗?

我想知道一些指导方针。感谢。

6 个答案:

答案 0 :(得分:4)

是的,接收类(Triangle)应该复制,除非设计是故意共享 List。

分享可能很有用,但却是例外。我不认为Triangle想要用其他东西分享它的顶点列表。

请注意,它仍然可以共享顶点(元素)。

答案 1 :(得分:2)

我个人同意Henk;你应该创建一个副本。

    /// <summary>
    /// Initialises a new instance of the <see cref="Triangle"/> class that 
    /// contains elements copied from the specified collection.
    /// </summary>
    /// <param name="vertices">
    /// The collection of vertices whose elements are to be copied.
    /// </param>
    public Triangle(IEnumerable<Vertex> vertices)
    {
        this.Vertices = new List<Vertex>(vertices);
    }

无论您选择什么,只需确保记录下来,以便消费者了解所期望的行为。

因此消费者知道他们可以安全地拨打new Triangle(vertices)

答案 2 :(得分:2)

C#是一种按值传递的语言,但由于列表是引用类型,因此它按值传递其引用。如您所述,这意味着您将列表的共享引用传递给类的构造函数。在代码中的任何位置进行的修改都会影响相同的列表。

这取决于您的班级所期望的行为是什么。如果要进行深层复制,最简单的方法是在构造函数中分配一个新列表,并将IEnumerable引用传递给列表的构造函数。

如果您想分享参考资料,这是一个完全有效的解决方案,只需确保您正确记录您的课程(或为您的课程命名)。

答案 3 :(得分:1)

在这种情况下,将List对象传递给构造函数将被视为设计不佳。也许更好的解决方案是使用方法

class Triangle
{
    List<Vertex> Vertices = new List<Vertex>(); // The triangle owns the vertex collection...

    public void SetVertices(IEnumerable<Vertex> vertices)
    {
        this.Vertices.Clear();

        this.Vertices.AddRange(vertices);
    }
}

答案 4 :(得分:1)

我会说这是一个文档问题。文档,即使它只是intellisense文档,应该说明是否使用给定列表初始化类,或者它是否将直接使用给定列表。给定任何可变引用类型,这是一个有效的问题,应该记录在案。

缺乏适当的文档,我会说这取决于你,这个班级的消费者,保护自己免受无证件的行为。你有两个选择:

  1. 找出应该告诉你的文档。您可以使用Reflector或简单实验来确定代码对您传递的可变对象执行的操作。

  2. 保护自己免受班级的行为,无论它是什么。如果一个类采用可变对象,则不要重用该对象。这样,即使班级的行为在以后发生变化,您也是安全的。

  3. 在您的具体情况下,我不认为Triangle类是错误的。它的构造函数可以采用IEnumerable<Vertex> 1 并使用这些值初始化成员List<Vertex>,相反,它的设计者选择了{{ 1}}直接使用它。这可能是基于绩效的决定。

    1 为了完整,如果有点迂腐,我应该提一下,即使花了List<Vertex>,你仍然会遇到同样的问题。该类仍然可以存储和重用对该对象的引用,因此对稍后对列表所做的更改很敏感。但是,在这种情况下,我认为要打破IEnumerable<Vertex>类。除了少数例外情况,公约规定采用Triangle的方法或构造函数将使用它一次然后丢弃它。

答案 5 :(得分:0)

您需要的是克隆或列表的深层副本。

请参阅cloning a list

的答案

this有关深层副本的更多信息,一般情况下