代码之家  ›  专栏  ›  技术社区  ›  rsenna

创建常量IEnumerable<TSomeType>的方法…?

  •  8
  • rsenna  · 技术社区  · 14 年前

    IEnumerable<TSomeType> ...?

    如果无法定义“最佳”方式,我有哪些选择?你的意见是什么,你认为有没有一种最合适的方法?

    • var enumerable = (IEnumerable<TSomeType>) new List<TSomeType> { Value1, Value2, Value3 };
    • var enumerable = (IEnumerable<TSomeType>) new TSomeType[] { Value1, Value2, Value3 };
    • (一些其他选项;例如Linq Select)。

    这里有一个问题-我们讨论的是一个非常受限的环境(一个安装了.NET的小型设备)。

    5 回复  |  直到 14 年前
        1
  •  9
  •   Jon Skeet    14 年前

    嗯,也不是 List<T> nor数组是不可变的,所以如果您真的在追求不可变,那么它们就不存在了——调用者可以强制转换结果,然后对其进行修改。

    列表<T> 把它包在 ReadOnlyCollection<T> . 如果没有东西再引用原始列表,那么它实际上是不可变的,除非反射。

    不会 投到 IEnumerable<T> GetEnumerator() .

    如果C编译器看到 foreach 语句覆盖数组,它生成直接转到索引器并使用 Length

    同样,如果你决定 ,保留为 列表<T> List<T>.Enumerator -这是一个结构-直接,没有拳击。

    编辑:史蒂夫梅格森提出了使用LINQ的观点。实际上,您可能可以做得更好,因为一旦您获得了底层列表的枚举器,就可以给出 安全地回到呼叫方,至少我知道所有的集合。所以你可以:

    public class ProtectedEnumerable<T> : IEnumerable<T>
    {
        private readonly IEnumerable<T> collection;
    
        public ProtectedEnumerable(IEnumerable<T> collection)
        {
            this.collection = collection;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            return collection.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
    

    也就是说只有一个 微小的 迭代时命中-仅对 GetEnumerator() Enumerable.Select ,这需要额外的授权 每一个 呼叫 MoveNext() (以及无操作投影)。

        2
  •  5
  •   Jackson Pope    14 年前

        3
  •  5
  •   Fredrik Mörk    14 年前

    Jon has answered

    即使您使列表不可变(例如,将其内容复制到 ReadOnlyCollection<T> ), 如果包含的对象不是不可变类型,则仍然可以操作它们的属性

    举个例子:

    class Person
    {
        public string Name { get; set; }
    }
    class Group
    {
        private readonly IEnumerable<Person> _persons;
        public Group(IEnumerable<Person> persons)
        {
            _persons = new ReadOnlyCollection<Person>(persons.ToList());
        }
        public IEnumerable<Person> Persons
        {
            get
            {
                foreach (var person in _persons)
                {
                    yield return person;
                }
            }
        }
    }
    

    List<Person> persons = new List<Person>( new[]{new Person { Name = "Fredrik Mörk" }});
    Group smallGroup = new Group(persons);
    Console.WriteLine("First iteration");
    foreach (var person in smallGroup.Persons)
    {
        Console.WriteLine(person.Name);
        person.Name += " [altered]";
    }
    Console.WriteLine("Second loop");
    foreach (var person in smallGroup.Persons)
    {
        Console.WriteLine(person.Name); // prints "Fredrik Mörk [altered]"
    }
    

    如你所见,尽管我们已经 列表 实际上是不可变的* Person 不是不可变类型。自从 Persons Group 类传递对实际 对象,调用代码可以轻松地操作对象。

    IEnumerable<T> 使用 yield return 分类为:

    class Person
    {
        public string Name { get; set; }
    
        // we add a copy method that returns a shallow copy...
        public Person Copy()
        {
            return (Person)this.MemberwiseClone();
        }   
    }
    
    class Group
    {
        private readonly IEnumerable<Person> _persons;
        public Group(IEnumerable<Person> persons)
        {
            _persons = new ReadOnlyCollection<Person>(persons.ToList());
        }
        public IEnumerable<Person> Persons
        {
            get
            {
                foreach (var person in _persons)
                {
                    // ...and here we return a copy instead of the contained object
                    yield return person.Copy();
                }
            }
        }
    }
    

    现在,上面的程序将 实例在列表中,但它自己的副本。不过,请注意,我们现在有什么可以称为 浅不变性 :如果 反过来,成员将不是不可变的,同样的问题存在于这些对象,等等。

    Immutability in C# Part One: Kinds of Immutability .

        4
  •  2
  •   Joshua Rodgers    14 年前

    你可以看看 ReadOnlyCollection<T> 包裹现有的 IList<T >为只读,可以强制转换为 IEnumerable<T>

    http://msdn.microsoft.com/en-us/library/ms132474.aspx

        5
  •  1
  •   Hal    14 年前

    我不认为常量和正则变量在对象的实例化中有什么不同。