【问题标题】:Make a list readonly in c#在 C# 中创建一个只读列表
【发布时间】:2015-05-21 14:40:58
【问题描述】:

我有这个示例代码。我要做的是使“Nums”值只能使用“AddNum”方法写入。

namespace ConsoleApplication1
{
    public class Person
    {
        string myName = "N/A";
        int myAge = 0;
        List<int> _nums = new List<int>();

        public List<int> Nums
        {
            get
            {
                return _nums;
            }
        }

        public void AddNum(int NumToAdd)
        {

            _nums.Add(NumToAdd);
        }

        public string Name { get; set; }
        public int Age { get; set; }
    }
}

不知何故,我尝试了很多关于 AsReadOnly() 和 readonly 关键字的事情,但我似乎无法让它做我想做的事情。

这是我必须访问该属性的代码示例。

Person p1 = new Person();
p1.Nums.Add(25); //access 1
p1.AddNum(37); //access 2

Console.WriteLine("press any key");
Console.ReadLine();

我真的希望“访问 1”失败,并且“访问 2”是可以设置值的唯一方式。提前感谢您的帮助。

【问题讨论】:

  • 您在自己的标签中指向它:使用ReadOnlyCollection 作为您的属性或只是在其中复制您的列表(这样用户可以更改他的副本但不能更改您的内部状态)
  • 尝试让 Nums 成为一个返回数组而不是 List 的函数。
  • 使用“私有列表 Nums”而不是公共列表 Nums
  • 我认为你不能。但是您可以删除属性“Nums”并提供一些从外部访问它的方法。

标签: c# list class readonly-collection


【解决方案1】:

√ 务必使用 ReadOnlyCollection,ReadOnlyCollection 的子类, 或者在极少数情况下 IEnumerable 用于属性或返回值 表示只读集合。

this article 的引述。

你应该有这样的东西:

List<int> _nums = new List<int>();

public ReadOnlyCollection<int> Nums
{
    get
    {
        return _nums.AsReadOnly();
    }
}

【讨论】:

    【解决方案2】:

    一般来说,集合类型的属性很差,因为即使集合包含在 ReadOnlyCollection 中,本质上也不清楚是什么:

    IEnumerable<int> nums = myPerson.Nums;
    myPerson.AddNum(23);
    foreach(int i in nums) // Should the 23 be included!?
      ...
    

    应该是这个意思。从Nums 返回的对象是它调用时存在的数字的快照,是实时视图吗?

    一种更简洁的方法是使用类似GetNumsAsArray 的方法,每次调用它都会返回一个新数组;在某些情况下,GetNumsAsList 变体也可能会有所帮助,具体取决于调用者想要对这些数字做什么。有些方法只适用于数组,有些只适用于列表,所以如果只提供上述其中一种,一些调用者将不得不调用它,然后将返回的对象转换为所需的类型。

    如果对性能敏感的调用者需要大量使用此代码,使用更通用的方法可能会有所帮助:

    int CopyNumsIntoArray(int sourceIndex, int reqCount, ref int[] dest, 
                          int destIndex, CopyCountMode mode);
    

    其中 CopyCountMode 表示代码应该做什么 从sourceIndex 开始的可用项目数大于或小于reqCount;该方法应该返回可用项目的数量,或者如果它违反了调用者的预期期望则抛出异常。一些调用者可能首先创建并传入一个 10 项数组,但如果要返回的项超过 10 个,则准备让该方法用更大的数组替换它;其他人可能期望正好有 23 个项目,并且没有准备好处理任何其他数字。使用参数指定模式将允许一种方法为多种调用者提供服务。

    尽管许多集合作者不会费心包含任何符合上述模式的方法,但在代码想要处理集合中的一小部分(例如 50,000 个集合中的 1,000 个项目)的情况下,这些方法可以大大提高效率)。在没有这样的方法的情况下,希望使用这样一个范围的代码必须要么要求整个事情的副本(非常浪费),要么单独请求数千个项目(也很浪费)。允许调用者提供目标数组将在同一方法进行许多查询的情况下提高效率,尤其是当目标数组足够大以放入大型对象堆时。

    【讨论】:

    • 嘿 supercat,我听懂了你所说的大约 40%。当我运行你在我的示例中给出的代码时:IEnumerable nums = myPerson.Nums; myPerson.AddNum(23); foreach(int i in nums) // 是否应该包括 23! ... 23 包括在内,你说它不应该是吗?.. 还是对我来说这是一个问题?
    • @stumped221:有些人只看调用代码(但看不到Nums 的实现)会期望包含 23;有些人会期望它不会被包括在内。应该尽可能地编写一个好的 API,以便只查看调用代码的人能够合理地猜出它的含义,但是 Nums 属性的行为不会让人感到惊讶很大一部分人在查看客户端代码。相比之下,如果代码调用GetNumsAsArray,任何看到方法调用的人都会期望它得到一个快照。
    • Hmmm.. 你能给我举个例子吗出现在该对象的实例中?我对 OOP 世界有点陌生,所以我真的只是在教育上问。
    • @stumped221:问题是调用者是否期望Nums 是接收它的对象的实时视图,或者表示与其分离的快照。打个比方,给定string foo=myControl.Text; myControl.Text+="*";,人们不会期望第二个语句会在foo 中添加一个星号,因为foo 表示控件的Text 属性在被调用时包含的内容的快照。跨度>
    • @stumped221:假设代码希望允许用户编辑数字列表,但提供恢复更改的选项。一种方法是在进行更改之前捕获列表的快照,并在用户选择“恢复”时从快照中恢复列表的状态。但是,如果应该是快照实际上是实时视图,那将行不通。有时调用者需要快照。有时他们可能需要实时取景。大多数情况下,调用者实际上都可以接受,仍应记录他们所获得内容的性质。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2016-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多