防止C#类的修改

3

你好,我是一个C#开发环境的新手。有人可以帮帮我吗?

修改Coffee类的实现方式,使得存储在Coffee中的列表不受原始列表或恢复列表的修改影响。

using System;
using System.Collections.Generic

public class Coffee 
{
    private List<int> info;
    public Coffee(List<int> info)
    {
      this.info = info;
    }
    public List <int> Restore()
    {
      return this.info;
    }
}

我不是在寻找答案(即喂食),而是需要指导,告诉我应该阅读什么内容。我是一名PHP开发者。提前感谢您的帮助。

2
了解复制 - 深复制和浅复制(对于整数,您只需要浅复制即可)。 - Gilad Green
请注意,这不是一个好的 Stack Overflow 问题,即使你已经找了很多资料,它仍然是一个“帮我做作业”/“给我一些参考”的问题... 在未来避免这些类型的问题。它们通常会被关闭和投票降低 ;) - Gilad Green
你不必担心,因为int是一个值类型,无论如何都会被“深度”复制。对于所有其他值类型也是如此。但是,如果你有一个类的一些实例的列表(引用类型),你需要进行深层复制。在这种情况下,有成千上万个关于这个问题的重复,只需搜索“深度克隆c#”即可。 - MakePeaceGreatAgain
@HimBromBeere - 有一半是正确的。通过返回对该列表项的引用,可以向其中添加项目。 - Gilad Green
您所说的对列表的修改,是指修改列表内部的元素(这在list<int>中甚至不可能实现),还是仅指对列表本身进行修改(添加、删除元素)? - MakePeaceGreatAgain
显示剩余2条评论
4个回答

4

您可以使用LINQ在输入时添加.ToList()来复制列表(这样您就有了自己的副本,不会受原始数据更改的影响),并在输出时也添加.ToList()(这样调用者就可以得到他们自己的副本,因此他们的更改不会影响您)。

using System;
using System.Collections.Generic

public class Coffee 
{
    private List<int> info;
    public Coffee(List<int> info)
    {
      this.info = info.ToList();
    }
    public List <int> Restore()
    {
      return this.info.ToList();
    }
}

1
这只能适用于我们有一个整数列表的情况,然而,正如Gilad Green所提到的那样,即使不调用ToList,它也可以正常工作。 - MakePeaceGreatAgain
我无法完全解析你的句子。为什么这是个List<int>很重要呢? - Colin Mackay
如果这是一个 List<MyClass>,那么你的方法只会创建原始列表的浅拷贝。 - MakePeaceGreatAgain
3
楼主说他们不希望修改列表,而不是不希望修改列表中的对象。浅复制对于楼主的要求足够了。而且对于不可变的对象(如int),深复制是不需要的,因为它是不可变的,不能被改变。 - Colin Mackay
3
这个问题是关于修改列表本身而不是其中包含的对象,所以我认为这是一个有效的答案。谈论需要进行深层克隆似乎是对问题的过度解读。 - Rob Hill

1
创建一个新的实例,类型为 List
Using System;
Using System.Collections.Generic

public class Coffee 
{
    private List<int> info;
    public Coffee(List<int> info)
    {
       this.info = new List<int>(info);
    }
    public List <int> Restore()
    {
       return new List<int>(this.info);
    }
}

2
为什么这个答案被下载了?它符合 OP 的要求,构造函数中传递的列表和 Restore 方法返回的列表的更改不会影响私有的 info 列表。要求是:_使存储在 Coffee 中的列表不受原始列表或恢复列表的修改影响_。 - Fabio
看起来我们不会得到有关投票反对的答案。 - Dmitry

0
如果您正在使用现代的.NET版本,则一种方法是返回一个ReadOnlyCollection<T>对象——这是一个包装器,它会在尝试修改时抛出异常。 List<T>.AsReadOnly()会创建这个包装器。但它不是静态类型安全的——它不能阻止您编写错误的代码。它绝对不能防止别人修改元素本身:roList [3] .name ="foo"; 另一种方法是复制列表。这是安全的,在旧版本的.NET中工作,但有开销。当插入元素时,新列表将被修改。由于它不是深拷贝,因此元素可以像先前的示例一样被更改。
第三种可能性是在返回之前将对象强制转换为IEnumerable<T>,它根本没有列表访问器API。您仍然可以使用foreach循环遍历元素。这实际上将阻止您编写非法代码,除非将对象转换回List<T>。这将使使用列表的代码稍微变慢和不太方便编写,因为API比List<T>更受限制。

这些方法都没有使用深拷贝,而深拷贝是唯一真正安全的防止对象修改的方式。由于许多类型的对象无法进行深拷贝,因此这不是可行的解决方案。


-2

你可以将它存储为 IReadOnlyList<int>

public class Coffee
{
    private IReadOnlyList<int> info;
    public Coffee(List<int> info)
    {
        this.info = info; // You can view original collection in readonly mode
    }
    public List<int> Restore()
    {
        return info.ToList(); // Makes a copy of original list
    }
}

更新:很抱歉错过了条件。如果您想存储列表的独立副本,可以使用Colin Mackay的解决方案。

PS:没有必要如此激进并立即对每个答案进行投票。这个代码示例也可能对新手理解C#有用。


1
列表的创建者在将其传递给咖啡类后仍然可以修改列表,这是其中之一的条件。 - Colin Mackay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接