通过接口函数添加项时类的IEnumerable成员的预期行为

本文关键字:成员 IEnumerable 接口 函数 添加 | 更新日期: 2023-09-27 18:16:41

我有一个类,它有一个IEnumerable。在这个类上,有一个接口函数,用于向这个IEnumerable集合Add新项。这个接口Add函数的工作方式是首先将当前集合转换为列表,添加一个新集合,然后将其转换回IEnumerable。现在IEnumerable的实例已经更改,并且对该类的该IEnumeraable成员的所有引用都是"解耦的"。

这只是一个选择的问题吗?还是保持IEnumerable引用的耦合是期望的行为?

是否有针对此类行为的编码准则?

请参阅下面演示解耦行为的示例代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new Factory();
            factory.AddPackageId(1);
            var referenceToPackageIds = factory.PackageIds;
            factory.AddPackageId(1);
            Console.WriteLine("referenceToPackageIds contains {0} packages.", referenceToPackageIds.Count());
            Console.ReadKey();
        }
    }
    class Factory
    {
        public IEnumerable<int> PackageIds
        {
            get
            {
                if (_PackageIds == null)
                {
                    _PackageIds = new List<int>();
                }
                return _PackageIds;
            }
            set
            {
                //Do some sanity checks here... but left out for brevity.
                _PackageIds = value;
            }
        }
        private IEnumerable<int> _PackageIds; 
        public void AddPackageId(int packageId)
        {
            var list = PackageIds.ToList();
            list.Add(packageId);
            PackageIds = list.ToArray();
        }
    }
}

通过接口函数添加项时类的IEnumerable成员的预期行为

您的问题只是一个更一般问题的特例:如果一个对象公开了一个返回类型提供只读视图的方法,那么返回的视图应该是:

  1. 一个不可变快照的视图,即使对象改变了,它也不会改变。

  2. 对象的"实时"只读视图,显示对对象所做的任何更改

  3. 只要对象没有被修改,就会反映对象的当前状态,但如果被修改,则不会对行为做出任何承诺的视图。

在许多情况下,#3将是该方法提供的最便宜的一个,并且它将满足大多数呼叫者的需求。调用方不应期望方法返回#1或#2,除非该方法的文档明确指定了哪个;为#3做好准备的呼叫者将对上述任何一个感到满意。

Java和.NET的创建者非常反对匈牙利表示法,这太糟糕了,因为像上面这样的区别在其他方面会代表它的出色用途。如果一个方法返回IEnumerable<T>,那么类型系统中没有任何东西会指示它返回上面哪种东西,但在试图编写高效正确的代码时,这些信息通常是至关重要的。我建议您明确决定是否希望向呼叫者承诺您返回的对象永远不会改变,或者是否明确避免做出这样的承诺。做出承诺将使一些调用方更加高效(因为想要快照的调用方将能够直接使用返回的对象,而不必将内容复制到新的不可变对象(,但可能会迫使类的未来版本复制可变对象的内容以返回它们,否则它将能够简单地返回只读包装器。