컬렉션에 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 ICollection및 IList인터페이스 와 인터페이스 호환이 가능합니다 . 즉, 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.