IEnumerable <T> 컬렉션에 항목을 추가하려면 어떻게해야합니까?


405

위의 제목으로 내 질문. 예를 들어

IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));

그러나 결국에는 1 개의 항목 만 있습니다.

우리는 같은 방법을 가질 수 있습니까 items.Add(item)?

같은 List<T>


4
IEnumerable<T>컬렉션 쿼리에만 사용됩니다. LINQ 프레임 워크의 중추입니다. 그것은 항상 같은 다른 콜렉션의 추상화 Collection<T>, List<T>또는, Array. 인터페이스는 한 번에 한 항목 씩 확장 모음을 걸을 수 GetEnumerator있는 인스턴스를 반환하는 메서드 만 제공합니다 IEnumerator<T>. LINQ 확장 방법은이 인터페이스에 의해 제한됩니다. IEnumerable<T>여러 컬렉션의 일부 집합을 나타낼 수 있으므로 읽기 전용으로 설계되었습니다.
Jordan

3
이 예에서는 다음 IList<T>대신에 선언하십시오 IEnumerable<T>.IList<T> items = new T[]{new T("msg")}; items.Add(new T("msg2"));
NightOwl888

답변:


480

IEnumerable<T>항목을 추가 할 수있는 컬렉션을 반드시 나타내는 것은 아니기 때문 입니다. 사실, 반드시 컬렉션을 나타내는 것은 아닙니다! 예를 들면 다음과 같습니다.

IEnumerable<string> ReadLines()
{
     string s;
     do
     {
          s = Console.ReadLine();
          yield return s;
     } while (!string.IsNullOrEmpty(s));
}

IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??

그러나 당신이 할 수있는 일은 열거되지 않은 오래된 객체의 모든 항목과 자신의 객체를 제공하는 지정되지 않은 유형 의 IEnumerable 객체를 만드는 것입니다. 당신은 그것을 위해 사용 Enumerable.Concat합니다 :

 items = items.Concat(new[] { "foo" });

이것은 배열 객체를 변경하지 않습니다 (어쨌든 배열에 항목을 삽입 할 수 없습니다). 그러나 배열의 모든 항목을 나열한 다음 "Foo"를 표시하는 새 객체를 만듭니다. 또한이 새 객체는 배열의 변경 사항을 추적합니다 (즉, 열거 할 때마다 항목의 현재 값이 표시됨).


3
단일 값을 추가하기 위해 배열을 만드는 것은 오버 헤드에서 약간 높습니다. 단일 값 Concat에 대한 확장 방법을 정의하는 것이 약간 더 재사용 가능합니다.
JaredPar

4
그것은 가치가 있지만 Concat, 루프에서 반복적으로 호출 되지 않는 한 조기 최적화라고 유혹하고 있습니다 . 그리고 그것이 있다면 어쨌든 더 큰 문제가 있습니다. 매번 Concat열거 자의 계층을 하나 더 얻었 기 때문에 단일 호출로 MoveNext인해 반복 횟수와 길이가 같은 호출 체인이 생깁니다.
Pavel Minaev

2
@ 파벨. 일반적으로 나를 귀찮게하는 배열을 만드는 데 메모리 오버 헤드가 아닙니다. 내가 짜증나는 여분의 타이핑입니다 :).
JaredPar

2
그래서 IEnumerable 이하는 일을 조금 더 이해합니다. 수정하려는 의도는 아닙니다. 감사합니다
ldsenow

7
IEnumerable<T>오버로드 operator+를 포함하여 C #에서 구문 기능 설탕에 대한 Connect 기능 요청이있었습니다 Concat(VB에서는 IEnumerable배열 인 것처럼 색인을 생성 할 수 있음을 알고 있습니까 -connect.microsoft.comElementAt ). /으로 VisualStudio / 피드백 / ... . Concat왼쪽과 오른쪽 모두에서 단일 항목을 가져 오려면 과부하가 두 번 더 발생 하면 입력이 줄어 듭니다. xs +=x 따라서 Mads (Torgersen)가 C # 5.0에서 우선 순위를 지정하십시오.)
Pavel Minaev

98

유형 IEnumerable<T>이 이러한 작업을 지원하지 않습니다. IEnumerable<T>인터페이스 의 목적은 소비자가 컬렉션의 내용을 볼 수 있도록하는 것입니다. 값을 수정하지 마십시오.

.ToList (). Add ()와 같은 작업을 수행하면 새 List<T>목록을 만들고 해당 목록에 값을 추가합니다. 원본 목록에 연결되어 있지 않습니다.

할 수있는 작업은 확장 추가 방법을 사용하여 추가 IEnumerable<T>된 값으로 새로 작성하는 것 입니다.

items = items.Add("msg2");

이 경우에도 원본 IEnumerable<T>객체 는 수정되지 않습니다 . 참조를 보유하여 확인할 수 있습니다. 예를 들어

var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");

이 작업 집합 후에도 변수 temp는 값 집합에서 단일 요소 "foo"를 가진 열거 형 만 참조하지만 항목은 "foo"및 "bar"값을 가진 다른 열거 형을 참조합니다.

편집하다

나는 Add가 일반적인 확장 방법이 아니라는 것을 잊어 버렸습니다. IEnumerable<T>왜냐하면 그것이 처음 정의하는 방법 중 하나이기 때문입니다. 여기있어

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}

12
그것은 불렀습니다 Concat:)
Pavel Minaev

3
@Pavel, 지적 해 주셔서 감사합니다. Concat조차도 다른 IEnumerable <T>이 필요하기 때문에 작동하지 않습니다. 프로젝트에서 정의한 일반적인 확장 방법을 붙여 넣었습니다.
JaredPar

10
Add비록 Add컬렉션이 아닌 다른 .NET 유형에서는 컬렉션을 제자리에서 변경 하기 때문에 호출 하지는 않습니다. 아마도 With? 또는의 또 다른 과부하 일 수도 있습니다 Concat.
Pavel Minaev

4
나는 일반적으로 변경 가능성을 암시하는 Concat것처럼 과부하가 더 나은 선택 일 것이라고 동의합니다 Add.
Dan Abramov

15
본문을 수정하는 것은 return e.Concat(Enumerable.Repeat(value,1));어떻습니까?
Roy Tinker

58

ICollection<T>또는 IList<T>인터페이스를 대신 사용해 보셨습니까? 또는 에 Add메소드 를 사용하려는 이유가 있습니다 IEnumerable<T>.

IEnumerable<T>실제 기본 개체가 항목 추가 / 제거를 지원하는지 여부를 반드시 보장하지 않고 유형을 '잘 표시, 열거 가능 또는 일련의 항목으로 표시'하는 데 사용됩니다. 또한 이러한 인터페이스는 구현 IEnumerable<T>되어 있으므로 모든 확장 방법을 사용할 수 IEnumerable<T>있습니다.


31

몇 가지 짧고 달콤한 확장 방법이 IEnumerable있고 IEnumerable<T>나를 위해합니다.

public static IEnumerable Append(this IEnumerable first, params object[] second)
{
    return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
    return first.Concat(second);
}   
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
    return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

우아함 (비 제네릭 버전 제외). 이러한 방법이 BCL에없는 것이 너무 나쁩니다.


첫 번째 Prepend 방법은 오류를 발생시킵니다. " 'object []'에는 'Concat'에 대한 정의가 포함되어 있지 않으며 'System.Linq.Enumerable.Concat <TSource> (System.Collections.Generic.IEnumerable <TSource>, System.Collections.Generic. IEnumerable <TSource>) '에 잘못된 인수가 있습니다 "
Tim Newton

@TimNewton 당신이 잘못하고 있습니다. 아무도 그 스 니펫에서 오류 코드를 알 수 없으므로 이유를 아는 사람. 팁 : ToString()더 많은 오류 정보를 캡처 하므로 항상 예외를 잡아서 처리 하십시오. 나는 당신이 include System.Linq;당신의 파일의 상단에 있지 않았지만 누가 압니까? 스스로 알아 내고 시도 할 수없는 경우 동일한 오류가 나타나는 최소 프로토 타입을 만든 다음 질문에 도움을 요청하십시오.

방금 위 코드를 복사하여 붙여 넣었습니다. 내가 언급 한 오류를주는 Prepend를 제외하고는 모두 정상적으로 작동합니다. 컴파일 타임 예외는 런타임 예외가 아닙니다.
Tim Newton

@ TimNewton : 나는 당신이 무슨 말을하고 있는지 완벽하게 작동하는지 모르겠습니다. 분명히 편집의 변경 사항을 무시하고 실수합니다.

아마도 우리 환경과는 다른 것입니다. VS2012 및 .NET 4.5를 사용하고 있습니다. 더 이상 시간을 낭비하지 마십시오. 작은 코드이지만 위의 코드에 문제가 있음을 지적했습니다. 어쨌든이 답변을 유용하다고 생각했습니다. 감사.
Tim Newton

25

.net Core Enumerable.Append에는 정확히 그렇게 하는 방법 이 있습니다.

메소드의 소스 코드는 GitHub에서 사용할 수 있습니다. .... 구현 (다른 답변의 제안보다 더 정교함)은 가치가 있습니다 :).


3
이것은 핵심뿐만 아니라 프레임 워크 4.7.1 이상입니다. docs.microsoft.com/en-us/dotnet/api/…
sschoof


22

IEnumerable은 항목 추가를 지원하지 않습니다.

'대체'는 다음과 같습니다.

var List = new List(items);
List.Add(otherItem);

4
당신의 제안이 유일한 대안은 아닙니다. 예를 들어, ICollection과 IList는 모두 합리적인 옵션입니다.
Jason Jason

6
그러나 인스턴스화 할 수도 없습니다.
폴 반 브 nk 크

16

두 번째 메시지를 추가하려면-

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})

1
그러나 Concat 메서드의 결과를 items 객체에 할당해야합니다.
Tomasz Przychodzki

1
네, 토마스가 맞습니다. 연결된 값을 항목에 할당하기 위해 마지막 표현식을 수정했습니다.
Aamol

8

상태와 같은 항목을 추가 할 수있을뿐만 아니라 List<T>기존 열거자가 있는 항목 (또는 거의 다른 비 읽기 전용 컬렉션)에 항목을 추가 하면 열거자가 무효화됩니다 ( InvalidOperationException그때부터 던져 집니다).

일부 유형의 데이터 쿼리에서 결과를 집계하는 경우 Concat확장 방법을 사용할 수 있습니다 .

편집 : 원래 Union예제 에서 확장명을 사용했지만 실제로는 올바르지 않습니다. 내 응용 프로그램은 광범위하게 사용하여 겹치는 쿼리가 결과를 복제하지 않도록합니다.

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);

8

Enumerable.Concat확장 방법 외에도 Enumerable.Append.NET Core 1.1.1에 다른 방법이있는 것 같습니다 . 후자는 단일 항목을 기존 순서에 연결할 수 있습니다. 따라서 Aamol의 답변은 다음과 같이 쓸 수도 있습니다.

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));

여전히이 함수는 입력 시퀀스를 변경하지 않으며 주어진 시퀀스와 추가 된 항목을 함께 묶는 래퍼 만 반환합니다.


4

다른 사람들은 이미 항목을 추가 할 수없는 이유에 대해 큰 설명을했습니다 IEnumerable. 컬렉션을 나타내는 인터페이스로 코딩을 계속하고 add 메소드를 원한다면 ICollectionor로 코딩해야한다고 덧붙일 것 IList입니다. 추가 된 보난자로서 이러한 인터페이스는를 구현 IEnumerable합니다.


1

당신은 이것을 할 수 있습니다.

//Create IEnumerable    
IEnumerable<T> items = new T[]{new T("msg")};

//Convert to list.
List<T> list = items.ToList();

//Add new item to list.
list.add(new T("msg2"));

//Cast list to IEnumerable
items = (IEnumerable<T>)items;

8
이 질문은 5 년 전에보다 완전한 방식으로 답변되었습니다. 질문에 대한 좋은 답변의 예로 받아 들여진 답변을보십시오.
pquest

1
마지막 줄은 다음과 같습니다 .items = (IEnumerable <T>) list;
Belen Martin

0

가장 쉬운 방법은 간단합니다.

IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");

그런 다음 IEnumerable 인터페이스를 구현하기 때문에 IEnumerable로 목록을 반환 할 수 있습니다


0

IEnumerable<int> Ones = new List<int>(); 다음 과 같이 객체를 생성 하면((List<int>)Ones).Add(1);


-8

물론, 가능합니다 (T-business를 제쳐두고 있습니다) :

public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
    List<string> list = items.ToList();
    string obj = "";
    list.Add(obj);

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