왜 C #에서 배열처럼 List를 초기화 할 수 있습니까?


131

오늘 나는 C #에서 내가 할 수 있다는 것을 알게되어 놀랐습니다.

List<int> a = new List<int> { 1, 2, 3 };

왜 이렇게 할 수 있습니까? 어떤 생성자가 호출됩니까? 내 수업으로 어떻게 할 수 있습니까? 이것이 배열을 초기화하는 방법이지만 배열은 언어 항목이며 목록은 간단한 객체입니다 ...


1
이 질문은 도움이 될 것입니다 : stackoverflow.com/questions/1744967/…
Bob Kaufman

10
꽤 멋지다? 사전을 초기화하기 위해 매우 유사한 코드를 수행 할 수도 있습니다.{ { "key1", "value1"}, { "key2", "value2"} }
danludwig

다른 관점에서 초기화 구문과 같은이 배열은 a의 기본 데이터 유형 List<int>이 실제로는 배열 임을 상기 시킵니다. 나는 C # 팀이 ArrayList<T>너무 분명하고 자연스럽게 들리는 이름 을 짓지 않는다는 사실을 싫어 합니다.
RBT

답변:


183

이것은 .NET의 컬렉션 초기화 구문의 일부입니다. 다음과 같은 경우 생성 한 컬렉션에서이 구문을 사용할 수 있습니다.

  • 그것은 IEnumerable(바람직하게는 IEnumerable<T>)

  • 이름이 지정된 메소드가 있습니다 Add(...)

기본 생성자가 호출 된 다음 Add(...)초기화 프로그램의 각 멤버에 대해 호출됩니다.

따라서이 두 블록은 거의 동일합니다.

List<int> a = new List<int> { 1, 2, 3 };

List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;

당신 이 원하는 경우, 예를 들어 성장 하는 동안 크기를 초과하지 않도록 대체 생성자를 호출 있습니다List<T> .

// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }

Add()방법은 단일 항목을 취할 필요가 없습니다. 예를 들어 두 가지 항목 Add()Dictionary<TKey, TValue>취하는 방법은 다음과 같습니다.

var grades = new Dictionary<string, int>
    {
        { "Suzy", 100 },
        { "David", 98 },
        { "Karen", 73 }
    };

대략 다음과 같습니다.

var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;

따라서 이것을 자신의 클래스에 추가하려면 언급 한 것처럼 IEnumerable(다시, 바람직하게 는) 구현 IEnumerable<T>하고 하나 이상의 Add()메소드를 작성하면됩니다 .

public class SomeCollection<T> : IEnumerable<T>
{
    // implement Add() methods appropriate for your collection
    public void Add(T item)
    {
        // your add logic    
    }

    // implement your enumerators for IEnumerable<T> (and IEnumerable)
    public IEnumerator<T> GetEnumerator()
    {
        // your implementation
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

그런 다음 BCL 컬렉션과 마찬가지로 사용할 수 있습니다.

public class MyProgram
{
    private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };    

    // ...
}

(자세한 내용은 MSDN을 참조하십시오 )


29
좋은 대답이지만 첫 번째 예에서 "정확하게 동일"하다고 말하는 것은 정확하지 않습니다. 그것은 정확히 동일 List<int> temp = new List<int>(); temp.Add(1); ... List<int> a = temp; 즉, a변수가 될 때까지 초기화되지 모든이라고 추가합니다. 그렇지 않으면 List<int> a = new List<int>() { a.Count, a.Count, a.Count };미친 짓을하는 것이 합법적 일 것입니다.
Eric Lippert

1
@ user606723 : List<int> a; a = new List<int>() { a.Count };List<int> a = new List<int>() { a.Count };
Joren

4
@Joren : 당신은 맞습니다; 사실 C # 사양 T x = y;은 다음과 같습니다 T x; x = y;.이 사실은 이상한 상황을 초래할 수 있습니다. 예를 들어 int x = M(out x) + x;합법적이므로 완벽하게 int x; x = M(out x) + x;합법적입니다.
Eric Lippert

1
@JamesMichaelHare 구현할 필요는 없습니다 IEnumerable<T>. 비 제네릭 IEnumerable은 컬렉션 이니셜 라이저 구문을 사용하기에 충분합니다.
phoog

2
IDisposable을 구현하는 일종의 컬렉션을 사용하는 경우 Eric의 요점이 중요합니다. 호출 using (var x = new Something{ 1, 2 })중 하나 Add가 실패하면 객체를 처리하지 않습니다 .
porges

11

그것은 구문 설탕 이라고 불립니다 .

List<T> "간단한"클래스이지만 컴파일러는 인생을 편하게하기 위해 특별한 대우를합니다.

이것을 소위 collection initializer 라고 합니다. 구현 IEnumerable<T>Add방법 이 필요합니다 .


8

C # 버전 3.0 사양 에 따르면 "컬렉션 이니셜 라이저가 적용되는 컬렉션 개체는 정확히 하나의 T에 대해 System.Collections.Generic.ICollection을 구현하는 형식이어야합니다."

그러나이 정보는이 글을 쓰는 시점에서 부정확 한 것으로 보입니다. 아래 주석에서 Eric Lippert의 설명을 참조하십시오.


10
해당 페이지가 정확하지 않습니다. 관심을 가져 주셔서 감사합니다. 그것은 어떻게 든 결코 삭제되지 않은 예비 시험판 문서 여야합니다. 원래 디자인에는 ICollection이 필요했지만 이것이 최종 디자인이 아닙니다.
Eric Lippert

1
명확하게 해 주셔서 감사합니다.
Olivier Jacot-Descombes


6

컬렉션 이니셜 라이저의 또 다른 멋진 점은 여러 가지 오버로드 된 Add메소드를 가질 수 있고 동일한 이니셜 라이저에서 모두 호출 할 수 있다는 것입니다! 예를 들어 다음과 같이 작동합니다.

public class MyCollection<T> : IEnumerable<T>
{
    public void Add(T item, int number)
    {

    }
    public void Add(T item, string text) 
    {

    }
    public bool Add(T item) //return type could be anything
    {

    }
}

var myCollection = new MyCollection<bool> 
{
    true,
    { false, 0 },
    { true, "" },
    false
};

올바른 과부하를 호출합니다. 또한 name을 사용하는 메소드 만 찾으면 Add리턴 유형이 무엇이든 될 수 있습니다.


0

구문과 같은 배열은 일련의 Add()호출 로 바뀌고 있습니다.

훨씬 더 흥미로운 예제에서 이것을 보려면 C #에서 처음으로 잘못된 두 가지 흥미로운 일을하는 다음 코드를 고려하십시오 .1) 읽기 전용 속성 설정, 2) 초기화와 같은 배열로 목록 설정.

public class MyClass
{   
    public MyClass()
    {   
        _list = new List<string>();
    }
    private IList<string> _list;
    public IList<string> MyList 
    { 
        get
        { 
            return _list;
        }
    }
}
//In some other method
var sample = new MyClass
{
    MyList = {"a", "b"}
};

이 코드는 완벽하게 작동하지만 1) MyList는 읽기 전용이며 2) 배열 이니셜 라이저로 목록을 설정했습니다.

이것이 작동하는 이유는 객체 초기화 프로그램의 일부인 코드에서 컴파일러는 항상 {}구문을 Add()읽기 전용 필드에서도 완벽하게 유효한 일련의 호출로 바꾸기 때문입니다.

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