C# 如何使 IEnumerable<T> 只读?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/359495/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:28:05  来源:igfitidea点击:

How to make IEnumerable<T> readonly?

c#.netgenericsienumerable

提问by gk.

Why are the lists list1Instanceand pin the Mainmethod of the below code pointing to the same collection?

为什么列表list1Instance和下面代码pMain方法指向同一个集合?

class Person
    {
        public string FirstName = string.Empty;
        public string LastName = string.Empty;

        public Person(string firstName, string lastName) {
            this.FirstName = firstName;
            this.LastName = lastName;
        }
    }

    class List1
    {
        public List<Person> l1 = new List<Person>();

        public List1()
        {
            l1.Add(new Person("f1","l1"));
            l1.Add(new Person("f2", "l2"));
            l1.Add(new Person("f3", "l3"));
            l1.Add(new Person("f4", "l4"));
            l1.Add(new Person("f5", "l5"));
        }
        public IEnumerable<Person> Get()
        {
            foreach (Person p in l1)
            {
                yield return p;
            }

            //return l1.AsReadOnly(); 
        }

    }  

    class Program
    {

        static void Main(string[] args)
        {
            List1 list1Instance = new List1();

            List<Person> p = new List<Person>(list1Instance.Get());           

            UpdatePersons(p);

            bool sameFirstName = (list1Instance.l1[0].FirstName == p[0].FirstName);
        }

        private static void UpdatePersons(List<Person> list)
        {
            list[0].FirstName = "uf1";
        }
    }

Can we change this behavior with out changing the return type of List1.Get()?

我们可以在不改变返回类型的情况下改变这种行为List1.Get()吗?

Thanks

谢谢

采纳答案by Mehrdad Afshari

In fact, IEnumerable<T>is already readonly. It means you cannot replace any items in the underlying collection with different items. That is, you cannot alter the referencesto the Personobjects that are held in the collection. The type Personis not read only, however, and since it's a reference type (i.e. a class), you can alter its members through the reference.

事实上,IEnumerable<T>已经是 readonly。这意味着您不能用不同的项目替换基础集合中的任何项目。也就是说,您不能更改对集合中保存的对象的引用PersonPerson然而,该类型不是只读的,而且由于它是一个引用类型(即 a class),您可以通过引用更改其成员。

There are two solutions:

有两种解决方案:

  • Use a structas the return type (that makes a copy of the value each time it's returned, so the original value will not be altered — which can be costly, by the way)
  • Use read only properties on the Persontype to accomplish this task.
  • 使用 astruct作为返回类型(每次返回时都会复制该值,因此不会更改原始值 - 顺便说一下,这可能代价高昂)
  • 使用Person类型上的只读属性来完成此任务。

回答by Szymon Rozga

They aren't pointing to the same .Net collection, but rather, to the same Personobjects. The line:

它们不是指向同一个 .Net 集合,而是指向相同的Person对象。线路:

List<Person> p = new List<Person>(list1Instance.Get()); 

copies all the Person elements from list1Instance.Get()to list p. The word "copies" here means copies the references. So, your list and IEnumerablejust happen to point to the same Personobjects.

将所有 Person 元素从 复制list1Instance.Get()到 list p。这里的“副本”一词是指复制参考文献。因此,您的列表IEnumerable恰好指向相同的Person对象。

IEnumerable<T>is alwaysreadonly, by definition. However, the objects inside may be mutable, as in this case.

IEnumerable<T>始终只读,顾名思义。但是,内部的对象可能是可变的,就像在这种情况下一样。

回答by tvanfosson

Return a new instance of Person that is a copy of pinstead of pitself in Get(). You'll need a method to make a deep-copy of a Person object to do this. This won't make them read only, but they will be different than those in the original list.

返回一个新的 Person 实例,它是Get() 中的一个副本p而不是p它自己。您需要一种方法来制作 Person 对象的深层副本来执行此操作。这不会使它们只读,但它们将与原始列表中的不同。

public IEnumerable<Person> Get()
{
    foreach (Person p in l1)
    {
        yield return p.Clone();
    }
}

回答by DonkeyMaster

IEnumerable<T>isreadonly

IEnumerable<T>只读的

pis a new collection which doesn't depend on list1instance. The mistake you made, is that you thought that this line list[0].FirstName = "uf1";
would only modify one of the lists, when on fact you're modifying the Personobject.
The two collections are distinct, they just happen to have the same items.
To prove that they are different, try adding and removing items from one of the lists, and you'll see that the other one isn't affected.

p是一个不依赖于list1instance. 您犯的错误是您认为这一行 list[0].FirstName = "uf1";
只会修改其中一个列表,而实际上您正在修改Person对象。
这两个集合是不同的,它们只是碰巧有相同的项目。
为了证明它们不同,请尝试从其中一个列表中添加和删除项目,您会看到另一个不受影响。

回答by BFree

First of all, your List in your class is public, so there's nothing stopping anyone from directly accessing the list itself.

首先,您班级中的 List 是公开的,因此没有什么可以阻止任何人直接访问列表本身。

Secondly, I would implement IEnumerable and return this in my GetEnumerator Method

其次,我将实现 IEnumerable 并在我的 GetEnumerator 方法中返回它

return l1.AsReadOnly().GetEnumerator();

回答by Amy B

You could make a deepclone of each item in the list, and never return references to your original items.

您可以对列表中的每个项目进行深度克隆,并且永远不要返回对原始项目的引用。

public IEnumerable<Person> Get()
{
  return l1
    .Select(p => new Person(){
      FirstName = p.FirstName,
      LastName = p.LastName
    });
}

回答by AnthonyWJones

If your person object is a real object then you should consider using an immutable version.

如果您的 person 对象是真实对象,那么您应该考虑使用不可变版本。

 public class Person
 {
     public FirstName {get; private set;}
     public LastName {get; private set;}
     public Person(firstName, lastName)
     {
         FirstName = firstName;
         LastName = lastName;
     }
  }

In this way its not possible to change the content of the instance once created and therefore it isn't important that existing instances are reused in multiple lists.

通过这种方式,一旦创建实例就无法更改其内容,因此在多个列表中重用现有实例并不重要。

回答by PJJ

This code returns a derived class, so as requested the return type hasn't changed.

此代码返回派生类,因此根据请求返回类型没有改变。

It does throw an error if you try and change a field (via property) so is 'read only'. If you did want to be able to change values without affecting the original the clone answer above is better.

如果您尝试更改字段(通过属性),它确实会引发错误,因此“只读”也是如此。如果您确实希望能够在不影响原始值的情况下更改值,那么上面的克隆答案会更好。

class  Person
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }


    public Person(string firstName, string lastName) {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

}

class PersonReadOnly : Person
{
    public override string FirstName { get { return base.FirstName; } set { throw new Exception("setting a readonly field"); } }
    public override string LastName { get { return base.LastName; } set { throw new Exception("setting a readonly field"); } }

    public PersonReadOnly(string firstName, string lastName) : base(firstName, lastName)
    {
    }
    public PersonReadOnly(Person p) : base(p.FirstName, p.LastName)
    {

    }

}

class List1
{
    public List<Person> l1 = new List<Person>();

    public List1()
    {
        l1.Add(new Person("f1", "l1"));
        l1.Add(new Person("f2", "l2"));
        l1.Add(new Person("f3", "l3"));
        l1.Add(new Person("f4", "l4"));
        l1.Add(new Person("f5", "l5"));
    }
    public IEnumerable<Person> Get()
    {
        foreach (Person p in l1)
        {
            yield return new PersonReadOnly(p);
        }
        //return l1.AsReadOnly(); 
    }

}  
class Program
{

    static void Main(string[] args)
    {
        List1 list1Instance = new List1();

        List<Person> p = new List<Person>(list1Instance.Get());           

        UpdatePersons(p);

        bool sameFirstName = (list1Instance.l1[0].FirstName == p[0].FirstName);
    }

    private static void UpdatePersons(List<Person> list)
    {
        // readonly message thrown
        list[0].FirstName = "uf1";
    }