ToList () — 새 목록을 작성합니까?


176

수업이 있다고 가정 해 봅시다

public class MyObject
{
   public int SimpleInt{get;set;}
}

그리고 List<MyObject>,을 가지고 있고 , ToList()중 하나를 변경 SimpleInt하면 변경 사항이 원래 목록으로 다시 전파됩니다. 즉, 다음 방법의 결과는 무엇입니까?

public void RunChangeList()
{
  var objs = new List<MyObject>(){new MyObject(){SimpleInt=0}};
  var whatInt = ChangeToList(objs );
}
public int ChangeToList(List<MyObject> objects)
{
  var objectList = objects.ToList();
  objectList[0].SimpleInt=5;
  return objects[0].SimpleInt;

}

왜?

추신 : 분명하게 밝혀지면 미안합니다. 하지만 지금은 나와 함께 컴파일러가 없습니다 ...


그것을 표현하는 한 가지 방법은 얕은 사본 을 .ToList()만드는 것 입니다. 참조는 복사되지만 새 참조는 여전히 원래 참조가 가리키는 것과 동일한 인스턴스를 가리 킵니다. 당신이 그것을 생각할 때 유형 이 언제 인지 만들 수 없습니다 . ToListnew MyObject()MyObjectclass
Jeppe Stig Nielsen 님이

답변:


217

예, ToList새 목록을 작성하지만이 경우 MyObject참조 유형 이므로 새 목록에는 원래 목록과 동일한 오브젝트에 대한 참조가 포함됩니다.

SimpleInt새 목록에서 참조 된 개체 의 속성을 업데이트하면 원래 목록의 해당 개체에도 영향을줍니다.

( MyObjectstruct아니라 선언 된 경우 class새 목록에는 원래 목록의 요소 사본이 포함되며 새 목록의 요소 특성을 업데이트 해도 원래 목록의 동등한 요소 에는 영향을 미치지 않습니다 .)


1
또한 List구조체 중 하나를 사용하면 같은 할당 objectList[0].SimpleInt=5이 허용되지 않습니다 (C # 컴파일 타임 오류). 리스트 인덱서의 get접근 자의 반환 값이 변수가 아니기 때문에 (구조 값의 반환 된 복사본 임) 할당 식으로 멤버 를 설정할.SimpleInt 수 없습니다 (보관되지 않은 복사본을 변경 함). . 어쨌든 누가 가변 구조체를 사용합니까?
Jeppe Stig Nielsen

2
@Jeppe Stig Nielson : 그렇습니다. 또한 마찬가지로 컴파일러는 다음과 같은 작업을 중지합니다 foreach (var s in listOfStructs) { s.SimpleInt = 42; }. 정말 좋아 당신이 뭔가를하려고 할 때 불쾌한 잡았다는 listOfStructs.ForEach(s => s.SimpleInt = 42)컴파일러가 예외없이 코드 실행을 허용하지만, 목록에있는 구조체가 변경되지 않고 남아있을 것입니다!
LukeH

62

반사판 소스에서 :

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return new List<TSource>(source);
}

따라서 원본 목록은 업데이트되지 않지만 (추가 또는 제거) 참조 된 개체는 업데이트됩니다.


32

ToList 컬렉션에 대한 후속 변경 사항을 반영하지 않는 새 목록을 항상 만듭니다.

그러나 객체 자체의 변경 사항을 반영합니다 (변경 가능한 구조체가 아닌 한).

즉, 원래 목록의 개체를 다른 개체로 ToList바꾸면 여전히 첫 번째 개체가 포함됩니다.
그러나 원래 목록에서 객체 중 하나를 수정해도 ToList동일한 (수정 된) 객체가 포함됩니다.


12

수락 된 답변은 그의 사례를 바탕으로 OP의 질문을 올바르게 해결합니다. 그러나 ToList콘크리트 수집에 적용 되는 경우에만 적용됩니다 . 소스 시퀀스의 요소가 아직 인스턴스화되지 않은 경우 (지연된 실행으로 인해) 유지되지 않습니다. 후자의 경우, 호출 할 때마다 ToList(또는 순서를 열거 할 때마다) 새 항목 세트를 얻을 수 있습니다 .

다음은이 동작을 보여주기 위해 OP 코드를 수정 한 것입니다.

public static void RunChangeList()
{
    var objs = Enumerable.Range(0, 10).Select(_ => new MyObject() { SimpleInt = 0 });
    var whatInt = ChangeToList(objs);   // whatInt gets 0
}

public static int ChangeToList(IEnumerable<MyObject> objects)
{
    var objectList = objects.ToList();
    objectList.First().SimpleInt = 5;
    return objects.First().SimpleInt;
}

위의 코드가 고안된 것처럼 보이지만이 동작은 다른 시나리오에서는 미묘한 버그로 나타날 수 있습니다. 작업이 반복적으로 발생하는 상황에 대해서는 다른 예 를 참조하십시오 .


11

예, 새 목록을 만듭니다. 이것은 의도적으로 설계된 동작입니다.

이 목록에는 원래 열거 가능한 시퀀스와 동일한 결과가 포함 되지만 영구적 인 (메모리 내) 컬렉션으로 구체화됩니다. 이를 통해 시퀀스 재 계산 비용을 발생시키지 않고 결과를 여러 번 사용할 수 있습니다.

LINQ 시퀀스의 장점은 합성 가능하다는 것입니다. 종종 IEnumerable<T>여러 필터링, 순서 및 / 또는 프로젝션 작업을 결합한 결과가 나타납니다. 확장 같은 방법 ToList()ToArray()당신이 표준 모음으로 계산 된 순서를 변환 할 수 있습니다.


6

새 목록이 작성되지만 그 목록의 항목은 원래 목록과 마찬가지로 원래 항목에 대한 참조입니다. 목록 자체에 대한 변경은 독립적이지만 항목에 대한 변경은 두 목록에서 모두 변경됩니다.


6

이 오래된 게시물을 우연히 발견하고 내 2 센트를 추가 할 생각이었습니다. 일반적으로 의심스러운 경우 모든 개체에서 GetHashCode () 메서드를 사용하여 ID를 확인합니다. 그래서 위의-

    public class MyObject
{
    public int SimpleInt { get; set; }
}


class Program
{

    public static void RunChangeList()
    {
        var objs = new List<MyObject>() { new MyObject() { SimpleInt = 0 } };
        Console.WriteLine("objs: {0}", objs.GetHashCode());
        Console.WriteLine("objs[0]: {0}", objs[0].GetHashCode());
        var whatInt = ChangeToList(objs);
        Console.WriteLine("whatInt: {0}", whatInt.GetHashCode());
    }

    public static int ChangeToList(List<MyObject> objects)
    {
        Console.WriteLine("objects: {0}", objects.GetHashCode());
        Console.WriteLine("objects[0]: {0}", objects[0].GetHashCode());
        var objectList = objects.ToList();
        Console.WriteLine("objectList: {0}", objectList.GetHashCode());
        Console.WriteLine("objectList[0]: {0}", objectList[0].GetHashCode());
        objectList[0].SimpleInt = 5;
        return objects[0].SimpleInt;

    }

    private static void Main(string[] args)
    {
        RunChangeList();
        Console.ReadLine();
    }

그리고 내 컴퓨터에 대답하십시오-

  • 목표 : 45653674
  • 목표 [0] : 41149443
  • 사물 : 45653674
  • 객체 [0] : 41149443
  • objectList : 39785641
  • objectList [0] : 41149443
  • whatInt : 5

따라서 본질적으로 목록이 전달하는 객체는 위 코드에서 동일하게 유지됩니다. 접근 방식이 도움이되기를 바랍니다.


4

ToList가 깊거나 얕은 복사를 수행하는지 묻는 것과 같습니다. ToList는 MyObject를 복제 할 수있는 방법이 없으므로 얕은 사본을 작성해야하므로 작성된 목록에는 원래 목록과 동일한 참조가 포함되므로 코드는 5를 리턴합니다.


2

ToList 새로운 목록을 만들 것입니다.

목록의 항목이 값 유형 인 경우 항목이 직접 업데이트되고 참조 유형 인 경우 변경 사항이 참조 된 오브젝트에 다시 반영됩니다.


2

소스 객체가 실제 IEnumerable 인 경우 (즉, 열거 형으로 패키지 된 컬렉션이 아닌) ToList ()는 원래 IEnumerable과 동일한 객체 참조를 반환하지 않을 수 있습니다. 새로운 객체 목록을 반환하지만 해당 객체는 다시 열거 될 때 IEnumerable이 생성 한 객체와 같거나 같지 않을 수 있습니다


1
 var objectList = objects.ToList();
  objectList[0].SimpleInt=5;

원본 객체도 업데이트됩니다. 새 목록에는 원래 목록과 마찬가지로 그 안에 포함 된 개체에 대한 참조가 포함됩니다. 요소를 변경하면 업데이트가 다른 요소에 반영됩니다.

이제 다른 목록에 반영되지 않는 목록을 업데이트 (항목 추가 또는 삭제)하는 경우.


1

ToList ()가 항상 새 목록을 반환한다고 보장되는 문서의 어느 곳도 보지 못했습니다. IEnumerable이 List 인 경우이를 확인하고 동일한 List를 반환하는 것이 더 효율적일 수 있습니다.

걱정은 때로는 반환 된 List가! = 원본 목록에 있는지 절대적으로 확신하고 싶을 수도 있습니다. Microsoft는 ToList가 새로운 List를 반환 할 것이라고 문서화하지 않았기 때문에 (다른 사람이 해당 문서를 찾지 않는 한) 확신 할 수 없습니다. 현재 작동하더라도 미래에도 변경 될 수 있습니다.

new List (IEnumerable enumerablestuff)는 새 List를 반환하도록 보장됩니다. 대신 이것을 사용합니다.



1
@RaymondChen IEnumerables에는 해당되지만 List에는 해당되지 않습니다. 에서 ToList호출 될 때 새 목록 객체 참조를 생성하는 것 같지만 ListMS 설명서에서는 이것이 보장되지 않는다고 말할 때 BenB가 옳습니다.
Teejay

ChrisS 소스 당 @RaymondChen 같이, IEnumerable<>.ToList()실제로으로 구현 new List<>(source)하고 대한 구체적인 재정이 없다 List<>, 그래서 List<>.ToList()실제로 새 목록 객체 참조를 반환 않습니다. 그러나 MS 문서에 따르면, 앞으로도 변경되지 않을 것이라는 보장은 없지만 대부분의 코드가 중단 될 수 있습니다.
Teejay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.