IT이야기

Enumerable.ElementAt() 대 [] 연산자를 사용하는 이유

cyworld 2021. 10. 3. 08:02
반응형

Enumerable.ElementAt() 대 [] 연산자를 사용하는 이유는 무엇입니까?


이것은 어리석은 질문처럼 보이지만 답을 찾지 못했으므로 여기에 있습니다. :)

두 경우 모두 컬렉션의 경계를 확인하지 못하면 "범위를 벗어남" 예외가 발생합니다. 이것은 단지 코딩 스타일 선호도입니까?

그리고 누군가가 예를 필요로 하는 경우:

List<byte> myList = new List<byte>(){0x01, 0x02, 0x03};
byte testByte = myList.ElementAt(2);

~ 대

byte testByte = myList[2];

왜냐하면 Enumerable은 더 일반적이고 enumerable이 나타내는 컬렉션에는 인덱서가 없을 수 있습니다.

그러나 사용 ElementAt()하는 경우 사용하지 마십시오. 아마도 효율적이지 않을 것입니다.


ElementAt()C#의 모든 열거형에 대한 통합 인터페이스를 제공합니다. 저는 공통 API를 좋아해서 꽤 자주 사용하는 편입니다.

기본 유형이 임의 액세스를 지원하는 경우(즉, []연산자 지원 ) ElementAt에서 이를 사용합니다. 따라서 유일한 오버헤드는 추가 메서드 호출(거의 관련성이 없음)입니다.

기본 유형이 임의 액세스를 지원하지 않는 경우 ElementAt()관심 있는 요소에 도달할 때까지 열거를 반복하여 시뮬레이션합니다. 이것은 매우 비쌀 수 있으며 때로는 부작용이 있습니다.

또한이 ElementAtOrDefault()종종 매우 편리하다.


특정 시나리오에서 ElementAt()를 사용하지 마십시오!!!

각 요소를 조회할 예정이고 500개가 넘는(또는 가질 수 있는) 경우 ToArray()를 호출하고 재사용 가능한 배열 변수에 저장하고 그런 식으로 색인을 생성합니다.

예를 들어; 내 코드는 Excel 파일에서 데이터를 읽고 있었습니다.
내 셀이 참조하는 SharedStringItem을 찾기 위해 ElementAt()를 사용하고 있었습니다.
500개 이하의 라인에서는 차이를 느끼지 못할 것입니다.
16K 라인의 경우 100초가 걸렸습니다.

상황을 악화시키려면(각 행을 읽을 때마다) 인덱스가 더 커지고 각 반복에서 더 많은 인덱싱을 수행해야 했기 때문에 필요한 것보다 더 오래 걸렸습니다.
처음 1,000개 행은 2.5초가 걸렸고 마지막 1,000개 행은 10.7초가 걸렸습니다.

이 코드 줄을 반복합니다.

SharedStringItem ssi = sst.Elements<SharedStringItem>().ElementAt(ssi_index);

이 기록된 출력 결과:

...Using ElementAt():
RowIndex:  1000 Read: 1,000 Seconds: 2.4627589
RowIndex:  2000 Read: 1,000 Seconds: 2.9460492
RowIndex:  3000 Read: 1,000 Seconds: 3.1014865
RowIndex:  4000 Read: 1,000 Seconds: 3.76619
RowIndex:  5000 Read: 1,000 Seconds: 4.2489844
RowIndex:  6000 Read: 1,000 Seconds: 4.7678506
RowIndex:  7000 Read: 1,000 Seconds: 5.3871863
RowIndex:  8000 Read: 1,000 Seconds: 5.7997721
RowIndex:  9000 Read: 1,000 Seconds: 6.4447562
RowIndex: 10000 Read: 1,000 Seconds: 6.8978011
RowIndex: 11000 Read: 1,000 Seconds: 7.4564455
RowIndex: 12000 Read: 1,000 Seconds: 8.2510054
RowIndex: 13000 Read: 1,000 Seconds: 8.5758217
RowIndex: 14000 Read: 1,000 Seconds: 9.2953823
RowIndex: 15000 Read: 1,000 Seconds: 10.0159931
RowIndex: 16000 Read: 1,000 Seconds: 10.6884988
Total Seconds: 100.6736451

참조를 위해 SharedStringItem을 저장할 중간 배열을 만든 후에는 시간이 100초에서 10초로 줄어들었고 이제 동일한 시간에 각 행을 처리합니다.

이 코드 줄:

SharedStringItem[] ssia = sst == null ? null : sst.Elements<SharedStringItem>().ToArray();
Console.WriteLine("ToArray():" + watch.Elapsed.TotalSeconds + " Len:" + ssia.LongCount());

다음 코드 줄을 반복합니다.

SharedStringItem ssi = ssia[ssi_index];

이 기록된 출력 결과:

...Using Array[]:
ToArray(): 0.0840583 Len: 33560
RowIndex:  1000 Read: 1,000 Seconds: 0.8057094
RowIndex:  2000 Read: 1,000 Seconds: 0.8183683
RowIndex:  3000 Read: 1,000 Seconds: 0.6809131
RowIndex:  4000 Read: 1,000 Seconds: 0.6530671
RowIndex:  5000 Read: 1,000 Seconds: 0.6086124
RowIndex:  6000 Read: 1,000 Seconds: 0.6232579
RowIndex:  7000 Read: 1,000 Seconds: 0.6369397
RowIndex:  8000 Read: 1,000 Seconds: 0.629919
RowIndex:  9000 Read: 1,000 Seconds: 0.633328
RowIndex: 10000 Read: 1,000 Seconds: 0.6356769
RowIndex: 11000 Read: 1,000 Seconds: 0.663076
RowIndex: 12000 Read: 1,000 Seconds: 0.6633178
RowIndex: 13000 Read: 1,000 Seconds: 0.6580743
RowIndex: 14000 Read: 1,000 Seconds: 0.6518182
RowIndex: 15000 Read: 1,000 Seconds: 0.6662199
RowIndex: 16000 Read: 1,000 Seconds: 0.6360254
Total Seconds: 10.7586264

보시다시피 Array로 변환하는 데 33,560개 항목에 대해 1초도 걸리지 않았으며 가져오기 프로세스의 속도를 높이는 데 충분한 가치가 있었습니다.


업데이트 : 나는 이것이 흥미로운 주제라고 느꼈고 그것에 대해 블로그에 올리기 로 결정 했습니다 .

기본적으로, 내 의견은 거의 랜덤 액세스에 대한 추상화를 활용 느낄 수 없다는 것입니다 없이 필요 IList<T>. 인덱스로 컬렉션의 요소에 무작위로 액세스하려는 코드를 작성하는 경우 해당 요소를 제공하는 코드가 필요합니다. IList<T>시작 해야 하는 ElementAt경우 인덱서 대신 사용할 의미가 없습니다 .

하지만 그것은 물론 제 생각일 뿐입니다.


또는 IList<T>와 같은 구현 에는 사용하지 마십시오 . 또는 와 같이 임의 액세스를 제공하지 않는 컬렉션 유형에 필요한 경우에만 사용하십시오 .T[]List<T>Queue<T>Stack<T>

var q = new Queue<int>();
var s = new Stack<int>();
for (int i = 0; i < 10; ++i)
{
    q.Enqueue(i);
    s.Push(i);
}

// q[1] would not compile.
int fromQueue = q.ElementAt(1);
int fromStack = s.ElementAt(1);

ElementAt()over 를 사용하는 유일한 이유 []IEnumerable대신 가 있거나 필요한 경우 입니다 IList.


ElementAt()IList<T>먼저 캐스트를 시도한 다음 인덱서를 사용하므로 의 성능 ElementAt()은 인덱서를 직접 사용하는 것과 크게 다르지 않을 것입니다. 따라서 , 가 있더라도 나중에 선언된 유형을 변경할 가능성이 있는 경우 IList<T>를 고려할 수 ElementAt()있습니다.


의미 론적 관점에서 둘 다 정확히 동일합니다. 어떤 것을 사용하든 프로그램의 의미를 바꾸지는 않을 것입니다.

당신은 사용 []:

  1. 열거 가능한 항목이 다음 같은 IList<T>이유로 선언 된 경우 :

    NS. 더 읽기 쉽고 관용적이며 널리 이해됩니다. 제가 사용하는 1순위 이유입니다.

    NS. 모든 인치를 최적화하고 싶습니다. []해야 변두리 가 직접 모습을 그리고 깁스를 피할 수 있기 때문에 더 성능이 좋은 ( 메모를 그 ElementAt용도 인덱서를이 경우 IEnumerable<T>이다 IList<T>따라서이 아니 큰 이득 ).

    씨. 당신은 여전히 ​​.NET 3.5 이전 시대에 있습니다.

당신은 사용 ElementAt:

  1. 경우 선언 된 유형은 단지입니다 IEnumerable<T>.

  2. 만약 ElementAt더 읽을 수 있습니다. 예를 들어 유창한 스타일의 호출에서:

    var x = GetThatList().ElementAt(1).Wash().Spin().Rinse().Dry();
    
    //is more readable than
    
    var x = GetThatList()[1].Wash().Spin().Rinse().Dry();
    
  3. if you are really adamant on using a consistent style for both IList<T> and IEnumerable<T>.

In simple terms, if you have the variable declared as IList<T> use [], otherwise use ElementAt (I stress the point "declared" because even if your IEnumerable<T> is an IList<T> underneath, you can only use ElementAt. Casting to IList<T> to use the indexer is a no no (it happens inside ElementAt anyway)). Readability triumphs. If you have that in mind its simple to choose. Indexer style is what I have to use in vast majority of cases. I'm forced to use ElementAt very rarely.


IEnumerable doesn't support direct index access, if you know you have a list, keep using the second form, since it is much easier to read (arguably).

ElementAt() is supported for all enumerations, not just lists - performance wise they are the same when used on a type of IList (i.e. a generic List), but if you have a list using the index access is more expressive. If you look at the source of ElementAt() with reflector, you will see that it will internally use the index access if the IEnumerable is of type IList:

..
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
    return list[index];
}
..

ReferenceURL : https://stackoverflow.com/questions/5326874/why-would-i-use-enumerable-elementat-versus-the-operator

반응형