16

以下のコードのリスト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";
        }
    }

の戻り値の型を変更せずに、この動作を変更できますList1.Get()か?

ありがとう

4

8 に答える 8

41

実際、IEnumerable<T> すでに readonlyです。つまり、基になるコレクション内のアイテムを別のアイテムに置き換えることはできません。つまり、コレクションに保持されているオブジェクトへの参照を変更することはできません。Personただし、型Personは読み取り専用ではなく、参照型 (つまり a class) であるため、参照を通じてそのメンバーを変更できます。

次の 2 つの解決策があります。

  • 戻り値の型としてa を使用しstructます (値が返されるたびに値のコピーを作成するため、元の値は変更されません — ちなみに、コストがかかる可能性があります)。
  • Personこのタスクを実行するには、型の読み取り専用プロパティを使用します。
于 2008-12-11T14:24:04.170 に答える
8

Get()pでそれ自体ではなくコピーである Person の新しいインスタンスを返します。pこれを行うには、Person オブジェクトのディープ コピーを作成するメソッドが必要です。これにより読み取り専用になるわけではありませんが、元のリストのものとは異なります。

public IEnumerable<Person> Get()
{
    foreach (Person p in l1)
    {
        yield return p.Clone();
    }
}
于 2008-12-11T14:29:38.713 に答える
2

それらは同じ .Net コレクションではなく、同じPersonオブジェクトを指しています。この線:

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

すべての Person 要素list1Instance.Get()を list にコピーしますp。ここでの「コピー」という言葉は、参照をコピーすることを意味します。したがって、リストとIEnumerableたまたま同じPersonオブジェクトを指しています。

IEnumerable<T>定義により、常に読み取り専用です。ただし、この場合のように、内部のオブジェクトは可変である場合があります。

于 2008-12-11T14:26:15.057 に答える
1

リスト内の各アイテムのディープクローンを作成し、元のアイテムへの参照を返すことはできません。

public IEnumerable<Person> Get()
{
  return l1
    .Select(p => new Person(){
      FirstName = p.FirstName,
      LastName = p.LastName
    });
}
于 2008-12-11T14:40:11.330 に答える
1

person オブジェクトが実際のオブジェクトである場合は、不変バージョンの使用を検討する必要があります。

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

このように、一度作成されたインスタンスの内容を変更することはできないため、既存のインスタンスが複数のリストで再利用されることは重要ではありません。

于 2008-12-11T14:42:59.750 に答える
0

まず第一に、クラス内の List は public であるため、だれもが直接 List 自体にアクセスすることを妨げるものは何もありません。

次に、IEnumerable を実装し、GetEnumerator メソッドでこれを返します。

return l1.AsReadOnly().GetEnumerator();
于 2008-12-11T14:33:03.717 に答える
0

IEnumerable<T> 読み取り専用です

pに依存しない新しいコレクションlist1instanceです。あなたが犯した間違いは、 実際にはオブジェクトlist[0].FirstName = "uf1";
を変更しているときに、この行がリストの 1 つだけを変更すると考えたことです。 2 つのコレクションは別個のものであり、たまたま同じアイテムを持っているだけです。 それらが異なることを証明するために、リストの 1 つに項目を追加および削除してみてください。もう 1 つのリストには影響がないことがわかります。Person

于 2008-12-11T14:30:34.453 に答える
0

このコードは派生クラスを返すため、要求どおりに戻り値の型は変更されていません。

フィールドを (プロパティを介して) 変更しようとするとエラーがスローされるため、「読み取り専用」です。元の値に影響を与えずに値を変更できるようにしたい場合は、上記のクローンの回答の方が優れています。

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";
    }
于 2018-11-28T01:53:58.367 に答える