如何使IEnumerable <T>只读?

gk.*_*gk. 12 .net c# generics ienumerable

为什么列表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";
        }
    }
Run Code Online (Sandbox Code Playgroud)

我们可以改变这种行为而不改变返回类型List1.Get()吗?

谢谢

Meh*_*ari 34

事实上,IEnumerable<T> 已经是readonly.这意味着您不能用不同的项替换基础集合中的任何项.也就是说,您无法更改对集合中保留的对象的引用Person.Person但是,该类型不是只读的,因为它是引用类型(即a class),您可以通过引用更改其成员.

有两种解决方案:

  • 使用a struct作为返回类型(每次返回时都会生成值的副本,因此原始值不会被更改 - 顺便说一下,这可能代价很高)
  • Person类型上使用只读属性来完成此任务.

  • 但是你可以把任何`IEnumerable <T>`强制转换回`T []`,`List <T>`,或者它实际上是什么类型,并修改它的项目(它们本身,而不是它们的属性). (5认同)
  • `IEnumerable <T>`本质上不是一个只读集合. (4认同)
  • @Shimmy如果我们要了解你能做什么的技术,你也可以使用反射访问任何类的私人成员并做任何你想做的事.只是因为你*可以*做某事(比如将'IEnumerable'转换为它的实际类型)并不意味着你**应该**. (2认同)

tva*_*son 7

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

public IEnumerable<Person> Get()
{
    foreach (Person p in l1)
    {
        yield return p.Clone();
    }
}
Run Code Online (Sandbox Code Playgroud)