[c#] 컬렉션에 AddRange

오늘 동료가 컬렉션에 범위를 추가하는 방법을 물었습니다. 그는에서 상속하는 클래스가 있습니다 Collection<T>. 이미 일부 항목을 포함하는 해당 유형의 가져 오기 전용 속성이 있습니다. 그는 다른 컬렉션의 항목을 속성 컬렉션에 추가하려고합니다. C # 3 친화적 인 방식으로 어떻게 그렇게 할 수 있습니까? (Union 수행 및 재 할당과 같은 솔루션을 방지하는 get-only 속성에 대한 제약에 유의하십시오.)

물론입니다. Property가있는 foreach입니다. 추가가 작동합니다. 그러나 List<T>스타일 AddRange는 훨씬 더 우아합니다.

확장 메서드를 작성하는 것은 쉽습니다.

public static class CollectionHelpers
{
    public static void AddRange<T>(this ICollection<T> destination,
                                   IEnumerable<T> source)
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

그러나 나는 바퀴를 재발 명하고 있다는 느낌이 있습니다. System.Linqor morelinq 에서 비슷한 것을 찾지 못했습니다 .

나쁜 디자인? 그냥 전화 추가? 명백한 것을 놓치고 있습니까?



답변

아니요, 이건 완벽하게 합리적으로 보입니다. 기본적으로이 작업을 수행 하는 List<T>.AddRange () 메서드가 있지만 컬렉션이 concrete이어야합니다 List<T>.


답변

루프를 실행하기 전에 확장 메서드에서 List로 캐스팅 해보십시오. 이렇게하면 List.AddRange의 성능을 활용할 수 있습니다.

public static void AddRange<T>(this ICollection<T> destination,
                               IEnumerable<T> source)
{
    List<T> list = destination as List<T>;

    if (list != null)
    {
        list.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}


답변

때문에 .NET4.5당신이 한 줄을 원하는 경우 사용할 수 System.Collections.Generic를 ForEach를.

source.ForEach(o => destination.Add(o));

또는 더 짧게

source.ForEach(destination.Add);

성능면에서는 각 루프 (구문 설탕)와 동일합니다.

또한 다음 과 같이 할당 하지 마십시오.

var x = source.ForEach(destination.Add) 

원인 ForEach은 무효입니다.

편집 : 코멘트에서 복사, ForEach에 대한 Lipert의 의견


답변

각각 Add은 컬렉션의 용량을 확인하고 필요할 때마다 크기를 조정합니다 (느림). 를 사용 AddRange하면 컬렉션이 용량을 설정 한 다음 항목을 추가합니다 (더 빠르게). 이 확장 방법은 매우 느리지 만 작동합니다.


답변

다음은 좀 더 고급 / 생산 준비 버전입니다.

    public static class CollectionExtensions
    {
        public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
            where TCol : ICollection<TItem>
        {
            if(destination == null) throw new ArgumentNullException(nameof(destination));
            if(source == null) throw new ArgumentNullException(nameof(source));

            // don't cast to IList to prevent recursion
            if (destination is List<TItem> list)
            {
                list.AddRange(source);
                return destination;
            }

            foreach (var item in source)
            {
                destination.Add(item);
            }

            return destination;
        }
    }


답변

C5 일반 컬렉션 라이브러리 클래스는 모든 지원 AddRange방법을. C5에는 기본 구현의 모든 기능을 실제로 노출하는 훨씬 더 강력한 인터페이스가 있으며 System.Collections.Generic ICollectionIList인터페이스 와 인터페이스 호환이 가능합니다 . 즉, C5의 컬렉션을 기본 구현으로 쉽게 대체 할 수 있습니다.


답변

IEnumerable 범위를 목록에 추가 한 다음 ICollection =을 목록에 설정할 수 있습니다.

        IEnumerable<T> source;

        List<item> list = new List<item>();
        list.AddRange(source);

        ICollection<item> destination = list;