C# 잡상: 2011-03-04

지난 번 글에 이어서. 이번 주에 든 생각 몇 가지 정리.

이번 주에는 대략 3일(반나절 단위로 세면 2일?) 정도는 C#을 가지고 프로그래밍 했다. 이때 했던 생각들 정리. 하루 종일 편두통에 시달리고 있으니 정말 간단간단히만 쓴다. 내일 기력을 회복하면 첨언 할지도 모름.

ArraySegment<T>의 기괴한 인터페이스

T[] 형식의 배열을 (배열, 시작 offset, 개수) 형식으로 저장하는 wrapper struct인데, 당연히 지원되리라 생각한 IEnumerable 인터페이스가 지원되지 않는다. 왜?

이걸 C#에 없는 pointer 연산에 대한 대용물 정도로 생각했는데 (C/C++이라면 그냥 begin, end 형태로 줄 테니까) 실제로도 그런 건 아니어서 인가; 그래서 ArraySegment 비슷한 일을 하는 다른 래핑 클래스를 만들고 IEnumerable<T> 를 구현했다. 역시 중력(?)을 줄이는 게 중요하다.

Generator

어제 작업하다 yield 구문을 써서 generator를 만들 수 있다는 걸 떠올렸다. 사실 이게 떠올랐다면 바로 위 주제는 큰 의미 없는 건데 Orz. C#에선 이걸 iterator block이라 부르더라.

이번 작업에서 tree 형태의 자료 구조를 traverse 하는 코드가 많았는데, 처음엔 C++ 류의 iterator 가 있었으면 하다가, yield구문 있는걸 떠올리곤, yield 쓰는 코드로 바꿨다. 자료 구조를 순회할 일이 많으면 (C++ 류의) iterator나 python generator 같은 문법이 확실히 편하다.

Task Parallel Library

몇 개의 독립적인 작업 + Producer-consumer 모델로 동작하는  작업이 있는 구조를 작성해야 했다. Collections.Concurrent 의 queue 써서 일일이 만들까 하다가1 TPL에 있는 Task 문법이 나름대로 편하길래 이걸로 만들고 꽤 만족했다. Fork-Join 형식이나 producer-consumer 모델처럼 자주 쓰는 구조는 매우 쉽게 짤 수 있는 듯. MS 개발 도구가 대부분 그렇듯, 자주 쓰는 시나리오는 정말 잘 된다.

다만 Go 쓰던 기억이랑 비교해보자면, go routine 에 해당하는 Task 부분은 비슷하거나 어떤 면에선 더 나은 수준으로 편하다. 다만 Go의 channel 에 해당하는 간결한 문법에 비견할 부분이 없는 건 아쉬움.

I/O 라이브러리의 인터페이스가 맘에 든다

대부분의 비동기 연산이 IOCP를 쓰는지 예외 던지면 보이는 스택 트레이스가 좀 무섭긴 하지만(…), 별도로 worker 스레드 없이 비동기 IO 편하게 할 수 있는 건 – 반쯤은 closure (delegate?) 덕이지만 – 괜찮았다.


  1. C++ 이라면 만들어 놓은 거 쓰거나 intel TBB로 했겠다. ↩︎