컬렉션에 AddRange


109

오늘 동료가 컬렉션에 범위를 추가하는 방법을 물었습니다. 그는에서 상속하는 클래스가 있습니다 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 에서 비슷한 것을 찾지 못했습니다 .

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


5
LINQ의 Q는 '쿼리'이며 실제로 데이터 검색, 프로젝션, 변환 등에 관한 것임을 기억하십시오. 기존 컬렉션을 수정하는 것은 실제로 LINQ의 의도 된 목적 영역에 속하지 않으므로 LINQ가 아무 것도 제공하지 않습니다. 이것에 대한 상자. 그러나 확장 방법 (특히 샘플)이 이에 이상적입니다.
Levi

한 가지 문제 ICollection<T>Add방법 이없는 것 같습니다 . msdn.microsoft.com/en-us/library/… 하지만 Collection<T>하나 있습니다.
Tim Goodman 2013

@TimGoodman-이것은 일반 인터페이스가 아닙니다. msdn.microsoft.com/en-us/library/92t2ye13.aspx
TrueWill

"기존 컬렉션을 수정하는 것은 실제로 LINQ의 의도 된 목적 영역에 속하지 않습니다." @Levi 그럼 왜 Add(T item)애초에 있었나요? 단일 항목을 추가 할 수있는 기능을 제공 한 다음 모든 호출자가 한 번에 둘 이상의 항목을 추가하기 위해 반복 할 것으로 기대하는 절반 정도의 접근 방식처럼 보입니다. 귀하의 진술은 확실히 사실 IEnumerable<T>이지만 저는 ICollections한 번 이상 좌절감을 느꼈 습니다. 나는 당신과 동의하지 않습니다.
akousmata

답변:


62

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


1
감사; 매우 사실이지만 대부분의 공공 자산은 MS 지침을 따르며 목록이 아닙니다.
TrueWill

7
그래-나는 왜 이것을하는 데 문제가 없다고 생각하는지에 대한 이론적 근거로 더 많이 제공했습니다. List <T> 버전보다 효율성이 떨어집니다 (list <T>가 미리 할당 할 수 있기 때문에)
Reed Copsey

.NET Core 2.2의 AddRange 메서드를 잘못 사용하면이 문제에 표시된대로 이상한 동작이 나타날 수 있습니다. github.com/dotnet/core/issues/2667
Bruno

36

루프를 실행하기 전에 확장 메서드에서 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);
        }
    }
}

2
as운영자는 던져하지 않습니다. destination캐스트 할 수없는 경우 listnull이되고 else블록이 실행됩니다.
rymdsmurf 2014

4
으악! 거룩한 모든 것에 대한 사랑을 위해 조건 가지를 바꾸십시오!
nicodemus13 jul.

13
사실 저는 진지합니다. 주된 이유는 그것이 종종 정말로 매우 어려운 추가인지 부하이기 때문입니다. 당신은 지속적으로 부정적인 조건을 평가하려고 노력하고 있습니다. 이것은 일반적으로 상대적으로 어렵습니다. 어쨌든 두 가지 모두를 가지고 있습니다. 'if null'은 이것을하고, 'else'는 반대하는 것보다 이것을하는 것이 더 쉽습니다. 또한 기본값에 관한 것입니다. 가능한 한 자주 긍정적 인 개념이어야합니다. .eg`if (! thing.IsDisabled) {} ​​else {} '는 중지하고'ah, not is disabled is enabled, right, 그래서 다른 분기는 비활성화되었을 때입니다). 구문 분석하기 어렵습니다.
nicodemus13

13
"something! = null"을 해석하는 것은 "something == null"을 해석하는 것보다 어렵지 않습니다. 그러나 부정 연산자는 완전히 다른 것이며 마지막 예제에서 if-else-statement를 다시 작성하면 해당 연산자 가 생략 됩니다. 이것은 객관적으로 개선 된 것이지만 원래 질문과는 관련이 없습니다. 이 특별한 경우에 두 가지 형식은 개인적인 선호도의 문제이며 위의 이유를 고려할 때 "! ="-연산자를 선호합니다.
rymdsmurf jul.

15
패턴 매칭은 ;-) ... 모든 사람의 행복을 만들 것입니다if (destination is List<T> list)
야곱 Foshee

28

때문에 .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의 의견


9
개인적으로 나는 이것에 대해 Lippert와 함께 있습니다 : blogs.msdn.com/b/ericlippert/archive/2009/05/18/…
TrueWill

1
source.ForEach (destination.Add) 여야합니까?
Frank

4
ForEach에서만 정의 할 것 List<T>,하지 Collection?
수호자 1

Lippert의 지금에서 찾을 수 있습니다 web.archive.org/web/20190316010649/https://...
user7610

Eric Lippert의 블로그 게시물 링크 업데이트 : Fabulous Adventures in Coding | "foreach"대 "ForEach"
Alexander

19

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


3
여기에 추가하려면 AddRange를 사용하는 하나의 대량 알림과 달리 각 추가에 대한 컬렉션 변경 알림도 있습니다.
Nick Udell 2014 년

3

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

    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;
        }
    }

rymdsmurf의 대답 은 순진하고 너무 단순 해 보일 수 있지만 이기종 목록에서 작동합니다. 이 코드가이 사용 사례를 지원하도록 할 수 있습니까?
richardsonwtr

예 : 추상 클래스 인의 destination목록입니다 Shape. 상속 된 클래스 source의 목록입니다 Circle.
richardsonwtr

1

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


0

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

        IEnumerable<T> source;

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

        ICollection<item> destination = list;

3
이 기능적으로 작동하지만, 그것은 수집 속성 (읽기 전용 만들기 위해 마이크로 소프트 지침을 나누기 msdn.microsoft.com/en-us/library/ms182327.aspx가 )
닉 Udell

0

또는 다음과 같이 ICollection 확장을 만들 수 있습니다.

 public static ICollection<T> AddRange<T>(this ICollection<T> @this, IEnumerable<T> items)
    {
        foreach(var item in items)
        {
            @this.Add(item);
        }

        return @this;
    }

그것을 사용하는 것은 목록에서 사용하는 것과 같습니다.

collectionA.AddRange(IEnumerable<object> items);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.