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
How to make IEnumerable<T> readonly?
提问by gk.
Why are the lists list1Instance
and p
in the Main
method of the below code pointing to the same collection?
为什么列表list1Instance
和下面代码p
的Main
方法指向同一个集合?
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 Person
objects that are held in the collection. The type Person
is 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。这意味着您不能用不同的项目替换基础集合中的任何项目。也就是说,您不能更改对集合中保存的对象的引用Person
。Person
然而,该类型不是只读的,而且由于它是一个引用类型(即 a class
),您可以通过引用更改其成员。
There are two solutions:
有两种解决方案:
- Use a
struct
as 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
Person
type to accomplish this task.
- 使用 a
struct
作为返回类型(每次返回时都会复制该值,因此不会更改原始值 - 顺便说一下,这可能代价高昂) - 使用
Person
类型上的只读属性来完成此任务。
回答by Szymon Rozga
They aren't pointing to the same .Net collection, but rather, to the same Person
objects. 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 IEnumerable
just happen to point to the same Person
objects.
将所有 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 p
instead of p
itself 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>
是只读的
p
is 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 Person
object.
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";
}