C# List Collection ToList()

Featured image

Git Source


우선, 필자는 List 클래스에서 ToList 확장 메서드를 호출하면 해당 객체 및 하위 엘리멘트들이 모두 Deep Copy가 되는지 알았는데 그게 아니였다. 😥

ToList()로 반환된 객체에서 값을 수정하니 원본 객체의 값도 수정이된 것을 확인했다.

해당 ToList()를 디컴파일러로 확인을 해본 결과 다음과 같았다.

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    return source != null ? new List<TSource>(source) : throw Error.ArgumentNull(nameof (source));
}
public List(IEnumerable<T> collection)
{
    if (collection == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    if (collection is ICollection<T> objs)
    {
        int count = objs.Count;
        if (count == 0)
        {
            this._items = List<T>._emptyArray;
        }
        else
        {
            this._items = new T[count];
            objs.CopyTo(this._items, 0);
            this._size = count;
        }
    }
    else
    {
        this._size = 0;
        this._items = List<T>._emptyArray;
        foreach (T obj in collection)
            this.Add(obj);
    }
}

ToList를 호출하면 결국 List 생성자를 호출하는데 대충 살펴보면 내부 필드인 _items에 CopyTo를 해주고 있다.

CopyTo 메서드도 결국에는 ICollection 인터페이스로 정의된 List.CopyTo가 호출되는데, 이 안에서 Array.Copy()를 호출해준다.

여기서 Array.Copy를 하면 MSDN 공식문서에 Deep Copy가 아닌 Shallow Copy가 이루어진다고 설명이 나와있다.

그렇기 때문에 ToList로 받아온 객체에서 수정을하면 원본도 수정이 되는 것..

무엇보다 List 클래스가 Linked List로 구현된줄 알았지만 Array로 구현이 되었다는 것이다. List의 추가 및 삭제가 이루어질 때마다 Array.Copy가 이루어지기 때문에 큰 객체를 담고 있거나 많은 객체가 있는 경우 그에 따른 비용이 커질 위험이 있을 것 같다.


Reference

Array.Copy 메서드