Thread에서 Task

스레드를 만드는 데는 비교적 많은 리소스가 필요하고 개별 스레드의 가상 메모리 소비량도 큰 편이다(기본 1MB). 앞에서 살펴 본 것처럼 스레드 풀을 이용하면 스레드를 만들어서 비동기 작업을 처리하고 작업이 끝난 스레드를 재사용할 수 있기 때문에 매번 새로 스레드를 만드는 것보다 효과적이다.

 닷넷 프레임워크 4에서는 비동기 작업을 시작할 때마다 운영체제 스레드를 만들어서 사용하는 대신 TPL이 태스크(Task)를 만들고 작업 스케줄러에게 처리할 비동기 작업이 있음을 통보하는 방식을 도입했다. 태스크 스케줄러를 이용하는 수많은 방법이 있겠지만 기본적으로 태스크 스케줄러는 스레드 풀에 작업 스레드를 요청한다. 스레드 풀은 효율성을 높이려고 현재 진행 중인 다른 작업이 끝난 다음에 요청된 작업을 처리하는 것이 낫다고 판단할 수도 있고, 혹은 요청된 비동기 작업을 처리하기 위한 작업 스레드를 특정 프로세서에 할당할 수도 있다. 스레드 풀은 또한 새 작업을 실행하는 일에 기존의 스레드를 재활용하는 것이 나을지 혹은 새 스레드를 만들어서 사용하는 것이 효과적일지도 판단한다.

 Task 개체의 안쪽에 내장하도록 비동기 작업을 추상화함으로써 TPL은 비동기 작업을 대변하는 개체를 제공하고 필요한 작업과 상호작용이 가능한 개체 지향 API를 제공할 수 있다. 그리고 작업의 단위를 표현하는 개체를 제공하므로 TPL은 프로그래밍을 통해 작은 작업들로 구성된 큰 작업을 워크플로우로 구성할 수 있으며, 이 점을 이후에 살펴본다.

 태스크는 비동기로 수행할 작업을 캡슐화하는 개체다. 코드를 대변하는 개체라는 점에서 대리자의 개념과 비슷하게 들릴 수 있다. 대리자는 동기적이며 태스크는 비동기적이라는 점에서 차이가 난다. , 예를 들어 Action과 같은 대리자를 실행하면 현재 스레드의 제어점은 즉시 대리자 코드로 이동하고 대리자의 실행이 끝날 때까지 제어를 반환하지 않는다. 이와 대조적으로 태스크는 처리해야 할 작업의 양과 무관하게 태스크 시작과 동시에 호출측으로 제어를 반환한다. 태스크는 비동기적으로 실행되며 보통 별도의 스레드를 이용한다.(이번 장의 뒤에서 살펴 볼 것인데, 사실 스레드 한 개만 이용해서 태스크들을 비동기적으로 실행하는 것이 가능하며 또 이득이 되기도 한다.) 태스크는 본질적으로 대리자를 동기적은 형태에서 비동기 실행 패턴으로 변환한다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

ManualResetEvent vs ManualResetEventSlim vs AutoResetEvent

재설정 이벤트에는 System.Threading.ManualResetEvent와 닷넷 프레임워크 4에서 추가된 간략 형태인 System.Threading.ManualResetEventSlim이 있다. 재설정 이벤트에서 제공하는 중요 메소드에는 Set() Wait()(ManualResetEvent에는 WaitOne())가 있다. Wait() 메소드를 호출하면 다른 스레드에서 Set()을 호출하거나 지정한 대기 시간이 끝날 때 까지 해당 스레드를 차단할 것이다.

ManualResetEventSlimManualResetEvent의 차이점은 후자의 경우 기본적으로 커널 동기화를 사용하도록 되어 있는 방면 전자는 피치 못할 경우를 제외하고는 커널을 통하지 않도록 최적화되어 있다. 따라서 ManualResetEventSlim이 더 많은 CPU 사이클을 사용할 가능성이 있지만 더 우수한 성능을 나타낸다. 그러므로 여러 개의 이벤트를 대기하거나 프로세스간 대기와 같은 요구가 없는 일반적인 경우에는 ManualResetEventSlim을 사용하자.

 System.Threading.AutoResetEvent라는 세 번째 재설정 이벤트가 있는데, 이것도 ManualResetEvent와 마찬가지로 스레드가 코드 상의 특정 위치까지 진행앴음을 다른 스레드에 신호(Set() 호출로 전달) 할 수 있게 해 준다. 차이점은 AutoResetEvent의 경우 신호를 받은 첫 번째 스레드가 자동 재설정 게이트를 통과하면 자동으로 다시 잠금이 설정되기 때문에 단 하나의 스레드만 차단해제한다는 것이다. 하지만 이와 같은 자동 재설정 이벤트를 사용할 때 생산자 스레드가 실수로 소비자 스레드의 개수보다 더 많은 반복을 할 위험성이 너무 쉽게 노출된다. 그러므로 일반적으로 Monitor Wait() / Pulse() 패턴 혹은 세마포어의 사용이 권장된다(특정 블록에 대해 참여할 수 있는 스레드가 n개보다 작은 경우). AutoResetEvent와 대조적으로 ManualResetEvent의 경우에는 Reset()을 명시적으로 호출하지 않으면 설정되기 전 상태(신호를 보내기 전)로 되돌아가지 않는다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

비동기 태스크

 

다중 스레드 프로그래밍을 복잡하게 하는 요인에는 다음과 같은 것들이 있다.

1.     비동기 작업의 완료 여부 감시 : 가급적 폴링이나 대기 이외의 방법을 통한 비동기 작업 완료 여부 판단이 필요하다.

2.     스레드 풀링 : 스레드 풀링을 이용하면 스레드를 시작하거나 제거하는 데 드는 비용의 많은 부분을 줄일 수 있다. 또한 너무 많은 스레드를 만들어서 스레드를 실행하는 것보다 콘텍스트 변경에 더 많은 시간을 낭비하게 되는 상황도 피할 수 있다.

3.     교착 상태 회피 : 서로 다른 두 개의 스레드가 보호되는 데이터에 동시에 접근하려고 할 때 발생할 수 있는 교착 상태를 방지해야 한다.

4.     데이터 엑세스에 대한 동기화와 연속되는 작업들에 대한 원자성 제공 : 작업을 동기화하면 여러 작업들을 단일 작업 단위로 묶고 다른 스레드와 연관된 작업을 적절하게 수행할 수 있다. 잠금 기능을 이용해서 두 스레드가 같은 데이터를 동시에 접근하지 못하도록 할 수 있다.

 

오래 실행되는 메소드는 비동기적으로 호출하도록 다중 스레드 프로그래밍을 이용할 필요가 있다. 이와 같은 시나리오들에 대응하려고 개발자들이 점점 더 많은 다중 스레드 프로그래밍을 하게 되면서 공통된 처리 시나리오 집합과 프로그래밍 패턴들이 속속 등장하고 있다.

C# 5.0에서는 닷넷 4.0 TPL을 이용하는 TAP과 같은 패턴과 이를 지원하기 위한 C# 언어의 새로운 구조로 인해 한층 강력한 프로그래밍이 가능해졌다. 이번 절과 다음 절에 걸쳐 우선 TPL 자체를 살펴 본 다음에 간편하게 TAP 프로그래밍이 가능하게 해주는 async/await 문맥적 키워드를 TPL과 사용하는 방법을 자세히 살펴 볼 것이다. 19장에서는 후반부 절반 정도를 할애해서 TPL C# 5.0을 사용할 수 없는 상황에서 알고 있어야 할 추가적인 다중 스레딩 패턴들을 살펴본다.

 

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

스레드 폴링

프로세서에 의존하는 작업에 효과적으로 프로세서 시간을 할당하려면 스레드 풀을 활용한다.

입출력 관련 혹은 장기간 수행되는 작업의 경우, 스레드 풀의 스레드를 할당하지 말고 TPL을 이용하라

 

앞서 성능에 대한 고려사항을 살펴본 것처럼 스레드를 과도하게 사용하면 오히려 성능에 악영향을 미칠 수 있다. 스레드는 비교적 비싼 대가를 요구하는 자원이며 스레드 콘텍스트 변경은 공짜가 아닐 뿐더러 시분할 방식을 이용해서 두 개의 작업을 병렬 처리 형태로 모의하는 것은 한 개씩 실행하는 방식에 비해서 훨씬 느릴 수 있다.

 이와 같은 문제점에 효과적으로 대응하려고 BCL은 스레드 풀을 제공한다. 스레드를 직접 할당하는 대신에 처리하고자 하는 작업이 무엇인지 스레드 풀에 요청할 수 있다. 작업이 끝나면 스레드를 종료하고 제거하는 대신 스레드 풀에 반납하는 방식을 이용하기 때문에 작업이 요청되었을 때 새로운 스레드를 할당하는 데 들어가는 비용을 절약할 수 있다.

훨씬 더 많은 작업들을 비동기 처리하는데 이와 같은 풀링(pooling) 기법을 적용하면 더욱 큰 효과를 얻을 수 있을 것이다. 이런 효율성을 얻을 수 있는 이유는 매번 비동기 호출이 있을 때마다 스레드를 다시 만들지 않고 계속해서 재사용하기 때문이다. 스레드 풀 방식을 사용할 때도 여전히 성능과 동기화 문제를 피할 수 없으므로 세심하게 다뤄야한다.

프로세서를 효과적으로 사용하려고 스레드 풀은 스레드 풀을 이용해서 처리하는 모든 작업이 적절한 시간 내에 끝나고 결과적으로 반환된 스레드를 다른 작업에 재사용할 수 있다는 가정 하에 동작한다. , 스레드 풀은 모든 작업이 비교적 짧은 시간에 끝날 것이라고 가정한다.(밀리초 혹은 초 단위로 끝나는 작업 정도를 의미하며, 몇 시간이나 혹은 며칠씩 처리해야하는 작업은 고려치 않는다.) 이와 같은 가정을 기반으로 성능과 관련한 초급 주제를 살펴 본 것처럼 스레드 풀은 개별 프로세서가 작업에 최대한의 능력을 발휘하고 다양한 작업을 비효율적인 시분할 없이 처리할 수 있다. 스레드 풀은 스레드 생성을 조절해서 프로세서 한 개에 너무 많은 스레드가 할당되지 않도록 함으로써 과도한 시분할이 발생하지 않게 한다. 하지만 이렇게 함으로써 스레드 풀의 스레드가 모두 사용 중인 경우에 대기 중인 작업에 지연이 발생할 수 있다. 장시간 수행하거나 혹은 I/O와 관련된 작업에 스레드 풀의 모든 스레드가 이용되고 있다면 대기 중인 작업에 지연이 있을 수 있다.

 개발자가 직접 조작할 수 있는 개체인 Thread Task와 달리 스레드 풀은 주어진 작업을 처리하는 스레드에 대한 참조를 제공하지 않는다. 그러므로 호출하는 스레드는 앞에서 살펴본 스레드 관리 함수들을 이용해서 동기화나 기타 각종 제어를 작업 스레드에 요구할 수 없다.

 스레드 풀은 오랜 시간이 필요하거나 다른 스레드와 동기화가 필요한 작업에는 적합하지 않다. 진정 우리에게 필요한 것은 스레드와 스레드 풀을 구현으로써 이용할 수 있는 상위 수준의 추상화 계층을 구현하는 것인데, 테스크 병렬 라이브러리(TPL)가 바로 이러한 것을 가능하게 해준다.

 

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

프로덕션 코드에서 스레드를 중단시키지 말자

 

프로덕트 코드에서는, 결과를 예측할 수 없고 프로그램을 불안정하게 만드는 스레드 강제 중단의 사용을 피하라.

 

 Thread 개체에서 제공하는 Abort() 메소드는 스레드를 제거하는 기능을 제공한다. 그러려고 Abort()는 런타임을 이용해서 대상 스레드 내에서 ThreadAbortException 예외가 발생하게 한다. 이 예외를 catch할 수 있지만, catch해서 무시하도록 처리한다고 해도 스레드가 실제로 제거되었는지 확인할 때 다시 발생된다. 이렇게 스레드를 강제로 중단하는 것이 바람직하지 않은 수많은 이유 가운데 몇 가지를 들어보면 다음과 같다.

l  이 메소드는 스레드를 중단시키는 시도를 할 뿐이지 그 시도의 성공을 보장하지 않는다. 예를 들어 스레드 내의 제어점이 현재 finally 블록(방해해서는 안 되는 리소스 해제를 위한 중요한 코드를 실행중일 수 있기 때문) 혹은 비관리 코드(비관리 코드 내부에서 중단 시킬 경우  CLR 자체에 문제가 생길 수 있다) 내부에 있는 경우 런타임이 ThreadAbortException을 발생시키지 않는다. 대신 런타임은 제어가 finally 블록을 벗어나거나 관리 코드로 반환되기를 기다렸다가 예외를 발생시킨다. 하지만 반드시 이렇게 처리된다는 보장이 없다. 중단하려는 스레드의 finally 블록에 무한 루프가 있을 수 있고, 역설적으로 애초에 finally 문의 무한 루프 때문에 스레드를 중단하려고 했을 수도 있다.

l  중단된 스레드가 lock 문으로 보호되는 임계 코드 내에 있을 수 있다. finally와 달리 lock 문은 예외를 막지 않는다. 따라서 진행 중인 임계 코드는 예외를 맞아 실행 중에 중단되고 lock 개체는 자동으로 해체되면서 임계 영역에 들어가려던 다른 코드의 진입이 허용된다. 이때 실행되다가 중단된 코드의 상태가 임계 영역에 그대로 남아 있게 되는데, 근본적으로 잠금이라는 개념을 이용하는 이유가 이런 상태(원자성을 제공하지 못하는 상태)를 막기 위한 것이라는 점을 떠올린다면 이것은 분명 잘못된 것이며, 스레드를 중단함으로 인해 스레드에 안전한 코드를 한 순간에 위험한 코드로 만들어 버릴 수 있다.

l  CLR은 스레드의 중단으로부터 내부 데이터 구조가 안전하게 보홈되을 보장하지만 BCL(Base Class Liabrary)의 경우는 그렇지 못하다. 따라서 운 나쁘게 좋지 않은 시점에 예외로 인해 스레드가 중단된다면 개발자가 애써 구축한 데이터 구조나 BCL에서 제공하는 데이터 구조는 망가질 수 있다. 다른 스레드의 코드나 중단되는 스레드의 finally 블록에 있는 코드에서 망가진 데이터를 참조할 수 있기 때문에 오류가 확산될 수 있다.

 

결론적으로 최후 수단이 아니라면 스레드를 강제 중단시켜서는 안 된다. , 전체 어플리케이션 도메인 혹은 프로세스를 비상 종료하는 경우의 일환으로 중단하는 경우에만 사용하는 것이 이상적이다. 다행히 태스크 기반의 비동기 방식은 스레드 강제 종료를 위한 더 단단하고 안전한 취소 패턴을 이용하고 있으며 뒤에서 소개할 비동기 작업 절에서 살펴본다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

프로덕션 코드에서 스레드를 유휴 상태로 만들어서는 안 된다

 

프로덕션 코드에서 Thread.Sleep()을 사용하지 마라.

 

Thread.Sleep() 정적 메소드는 현재 스레드를 유휴 상태로 만드는데, 주어진 시간 동안 운영체제로 하여금 현재 스레드에 시간을 할당하지 않게 된다. 밀리초 단위의 숫자 혹은 TimeSpan 형식의 매개변수를 통해서 실행을 재개하기 전에 운영체제가 대기해야 할 시간을 지정한다. 대기하는 동안에 운영체제는 대기 중인 다른 스레드에 시간을 할당한다. 꽤 그럴듯하게 들리지만 이런 상황에 대처하기 위한 더 나은 설계가 있을 것 같은 나쁜 코드 냄새가 난다.

 스레드는 종종 여러 이벤트와 보조를 맞추려고 유휴 상태에 놓이곤 한다. 하지만 운영체제는 이와 관련된 시점 조절과 관련해 정확성을 일체 보장하지 않는다. , ‘123 밀리초 동안 유휴 상태로 유지해 주세요라고 요청했을 때, 운영체제는 해당 스레드를 적어도 123 밀리초 동안 유휴 상태로 만드는 데 훨씬 더 오래 걸릴 가능성도 있다. 스레드가 유휴 상태로 진입하고 또 벗어나는 데 걸리는 시간은 절대적인 것이 아니며 임의의 시간이 소요될 수 있다. Thread.Sleep()을 고정밀 타이머처럼 사용해서는 안 되는 이유다.

 더 심한 경우는 Thread.Sleep()바보들의 동기화 시스템으로 사용하는 것이다. 예를들어 일정량의 비동기 작업을 수행해야 하고 이 비동기 작업이 끝나기 전에는 현재의 스레드를 더 이상 진행해서는 안 되는 상황을 가정해 보자. 이때 비동기 작업이 끝날 것으로 예상되는 대략적인 시간보다 충분히 더 여유를 잡아서 스레드를 유휴 상태로 만들고 스레드가 유휴 상태에서 깨어났을 때 비동기 작업이 끝났을 것이라는 위험한 가정을 기준으로 개발을 시도할 수 있다. 이것은 정말 위험한 생각이다. 비동기 작업은 여러분이 생각하는 것 보다 더 긴 시간을 요할 수 있다. 이와 같은 상황에 적합한 스레드 동기화 메커니즘을 다음 장에서 살펴본다.

 스레드를 유휴 상태로 만드는 것 자체도 나쁜 프로그래밍 습관이라고 볼 수 있다. 일단, 스레드가 유휴 상태에 들어가면 스레드에서 포함하고 있는 코드는 원해도 실행할 수 없는 무응답 상태가 되기 때문이다. 윈도우 응용 프로그램의 주 스레드를 유휴 상태로 만들면 사용자 인터페이스에서 발생하는 메시지도 처리하지 않게 되기 때문에 결국 프로그램은 응답이 없는 상태에 빠진다.

 더 일반적인 시각에서 보면 스레드와 같은 값비싼 리소스를 할당해 놓고 작업을 하지 않게 하는 것은 역시 바람직한 프로그래밍이 아니다. 아무도 잠만 자는 직원에게 월급을 주지 않으려 할 것 이다. 따라서 수백만이나 수십억에 이르는 프로세서 주기를 허송세월로 흘려보내려고 힘들게 스레드를 할당하지 않는 것이 바람직하다.

 한편, Thread.Sleep()을 적절하게 사용하는 경우도 물론 있다. 유휴 대기 시간을 0으로 설정해 호출하는 방식으로, 운영체제에게 남아있는 시간 할당을 포기할 의사를 전달해서 대기 중인 다른 스레드로 순서를 재빨리 넘기도록 할 수 있다. 이렇게 양보한 스레드는 추가적인 대기 시간 없이 다시 일반적인 스케줄에 따라 작업을 진행한다. 다음으로 테스트 코드에서는 종종 실제로 프로세서에 의미 없는 연산을 시켜 부하를 주는 방법 대신 Thread.Sleep()을 이용해서 일부 장기간의 대기 시간을 요하는 작업을 모의하기도 한다. 다른 이유로 스레드를 유휴 상태로 만들고 있다면 원하는 기능을 구현할 수 있는 더 좋은 방안이 없는지 조심스럽게 검토해 보는 것이 좋다.

 C# 5.0의 태스크 기반 비동기 프로그래밍에서는 Task.Delay() 메소드의 결과에 await 연산을 이용해서 현재 스레드의 진행에 영향을 주지 않고 비동기 지연 처리가 가능하다. 자세한 내용은 다음 장의 타이머 절을 참고하라.

 

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

평소에 강한 참조에 대해 별로 신경안썼는데...... 

 

WeakReference Class를 이용해 테스트 해보았다.

 

 

 

결론은 삭제 역할을 하지 않는 개체의 필드에 삭제될 개체를 놓게 된다면 강한 참조가 일어난다.

그 후 개체가 삭제 된 후에도가비지 컬렉터가 제거를 하지 못해서 Memory Leak 또는 잘못 사용 될 수 있으니

WeakReference Class의 약한 참조를 이용

 

 

    class Man

    {

    }

    class Program

    {

        static void Main(string[] args)

        {

            TestStringReference();

            TestWeakReference();

 

            Console.ReadKey();

        }

        /// <summary>

        /// WeakReference Class 이용해 약한 참조 테스트

        /// 참조 중인 개체는 null이됨

        /// </summary>

        private static void TestWeakReference()

        {

            Man m = new Man();

            WeakReference refMan = new WeakReference(m); //약한참조

            m = null;

            GC.Collect();

 

            Console.WriteLine("약한 참조 원본: {0}", (m == null ? "죽었음" : "살았음"));

            Console.WriteLine("약한 참조 참조: {0}", (refMan.Target == null ? "죽었음" : "살았음"));

        }

 

        /// <summary>

        /// 강한 참조 테스트

        /// 참조 중인 개체는 null 되지 않음

        /// </summary>

        private static void TestStringReference()

        {

            Man m = new Man();

            Man refMan = m; //강한 참조

            m = null;

            GC.Collect();

 

            Console.WriteLine("강한 참조 원본: {0}", (m == null ? "죽었음" : "살았음"));

            Console.WriteLine("강한 참조 참조: {0}", (refMan == null ? "죽었음" : "살았음"));

        }

    }

 

 

 

 

         결과화면 :

 

 

저는 event 예외 처리 할 때 아래와 같이 진행해왔었습니다.

SomethingEvent 이벤트를 아무도 등록하지 않았을때의 예외처리입니다.

 

 

 

public event EventHandler SomethingEvent;

 

        public void RaiseSomethingEvent()

        {

            if (this.SomethingEvent != null)

                this.SomethingEvent(null,null);

        } 

 

 

 

 

아래와 같이 빈 delegate를 이용하면 null이 아니기 때문에 event를 등록한 개체가 없더라도 예외가 발생하지 않습니다.

멀티쓰레드 환경도 기존의 코드와 달리 null 체크를 하지 않기 때문에 아무런 문제없이 잘동작합니다.

 

 

public event EventHandler SomethingEvent = delegate { };

 

        public void RaiseSomethingEvent()

        {

            this.SomethingEvent(null,null);

        } 

 

 

 

아래의 코드도 동일하게 잘 동작합니다.

 

 

 

public event Action<string> SomethingEvent = delegate { };

 

        public void RaiseSomethingEvent()

        {

            this.SomethingEvent("foo");

        }

 

 

 

참고 : Pro Wpf and Silverlight MVVM - Chapter 5. Event and Commands (112p)

왼쪽이 키보드, 오른쪽이 Keys 열거형 이름입니다.

~,` -> Oemtilde
!,1 -> D1
@,2 -> D2
...
),0 -> D0
_,- -> OemMinus
+,= -> OemPlus
|,\ -> OemPipe
← (BackSpace) -> Back
{,[ -> OemOpenBrackets
},] -> OemCloseBrackets
Enter -> Return
Caps Lock -> Capital
:,; -> OemSemicolon
",' -> OemQuotes
Shift -> ShiftKey (오른쪽 왼쪽 다 인식합니다. LShiftKey, RShiftKey도 있습니다.)
< ,, -> Oemcomma
> ,. -> OemPeriod
?,/ -> OemQuestion
Ctrl -> ControlKey (Shift와 마찬가지)
왼쪽 윈도우키 -> LWin
Alt -> Menu (Shift와 마찬가지)
한자 -> HanjaMode
SpaceBar -> Space
한/영 -> KanaMode
오른쪽 윈도우키 -> RWin
마우스 오른쪽 버튼 메뉴 기능의 키(오른쪽 윈도우키 옆에 있는 키) -> Apps
오른쪽 Ctrl -> HanjaMode (Ctrl, RControlKey로도 작동하고, HanjaMode로도 작동합니다.)
PageUp -> PageUp, Prior
PageDown -> PageDown, Next
↑ -> Up
← -> Left
↓ -> Down
→ -> Right

키패드 있는 곳의 키들
/ -> Divide
* -> Multiply
- -> Subtract
+ -> Add
0~9까지 키패드의 숫자들 -> NumPad0 ~ NumPad9
. -> Decimal

 

 

 

참조 : http://sjpison.tistory.com/10

 

 

 

 

 

C# 4.0 선택적 매개변수, 명명된 매개변수

 

대전에서 프로젝트를 진행 할 때는 VS2008 C#3.0을 사용해서 잘 몰랐던 부분을

서울에 올라와서 VS2012 C#4.0을 사용하면서 편리함을 느끼고 있네요!

물론 먼저 Raw한 부분부터 경험하는 것은 중요한거죠?^.^ EndUser의 프레임워크 설치도 귀찮게하니..

 

 

C++ 을 보면 Default 매개변수라는 개념이 있습니다.

매개변수를 넘기지 않으면 해당 매개변수는 Default값으로 사용하겠다는 개념입니다.

예를들어 C++ 같은 경우에는 아래와 같이 메서드가 정의되어 있을   Foo(); 요런식으로 호출이 가능합니다. aDefault 값은 0으로 지정해두었으니 정수형 값 1 Return되겠네요.

int Foo(int a=0)

{

     return a+1;

}

 

C#에서는 Default 매개변수라는 개념이 C#4.0 이전까지는 없었습니다.

너무 불편했었습니다….. 예를들어 아래의 코드를 보시면

주소록에 이름과 전화번호를 추가할 때 전화번호는 일단 입력하고 싶지 않을 때,

아래와 같이 오버로딩을 이용하였었습니다.

class Program

    {

        class Man

        {

            public string Name { get; private set; }

            public string CellPhone { get; private set; }

 

            public Man(string name, string cellPhone)

            {

                this.Name = name;

                this.CellPhone=  cellPhone;

            }

        }

 

        class AddressBook

        {

            public List<Man> Mans { get; private set; }

 

            public void CreateMan(string name, string cellPhone)

            {

                Man man = new Man(name, cellPhone);

                this.Mans.Add(man);

            }

            public void CreateMan(string name)

            {

                this.CreateMan(name, "None");

            }

        }

 

        static void Main(string[] args)

        {

            AddressBook addressBook = new AddressBook();

            addressBook.CreateMan("경찰서", "112");

            addressBook.CreateMan("소방서", "119");

            addressBook.CreateMan("새로알게된사람");

        }

    }

 


                                   

오버로딩의 대안을 제시한 새로운 명명된 매개 변수와 선택적 매개 변수 기능이 C# 4.0에 나왔습니다.  위의 코드는 아래와 같이 선택적 매개변수로 깔끔하게 변하였습니다. , 선택적 매개변수는 선택적이지 않은 매개변수의 무조건 뒤쪽에 붙어야 합니다.

        class AddressBook

        {

            public List<Man> Mans { get; private set; }

 

            public void CreateMan(string name, string cellPhone = "None")

            {

                Man man = new Man(name, cellPhone);

                this.Mans.Add(man);

            }

        }

 

        static void Main(string[] args)

        {

            AddressBook addressBook = new AddressBook();

            addressBook.CreateMan("경찰서", "112");

            addressBook.CreateMan("소방서", "119");

            addressBook.CreateMan("새로알게된사람");

        }  

 

 

위의 코드에서 아래와 같이 주소도 추가하였습니다.

public void CreateMan(string name, string cellPhone = "None", string address="None")

{

Man man = new Man(name, cellPhone);

this.Mans.Add(man);

}

 

애매하지요. 이름과 주소만 추가하고싶은데 가운데 전화번호는 어떻게하지?

명명된 매개변수를 이용하면 처리가 가능합니다.  

매개변수명: 매개변수

위와 같이 진행하시면 원하는 매개변수를 지정하여 원하는 데이터를 넘길 수 있습니다.

관심이 없는 매개변수는 그냥 내버려두시면 됩니다.

class AddressBook

{

    public List<Man> Mans { get; private set; }

 

    public void CreateMan(string name, string cellPhone = "None", string address="None")

    {

        Man man = new Man(name, cellPhone);

        this.Mans.Add(man);

    }

}

 

static void Main(string[] args)

{

    AddressBook addressBook = new AddressBook();

    addressBook.CreateMan("경찰서", "112");

    addressBook.CreateMan("소방서", "119");

    addressBook.CreateMan("우리집", "031", "경기도");

    addressBook.CreateMan("새로알게된사람", address: "경기도");

}

 

 

 

소스 : AboutOptionParam.zip

출처 : MSDN - C# 4.0

'.Net > C#' 카테고리의 다른 글

C# null조건 검사하지 않고 event 발생키시기  (0) 2013.07.30
C# Keys 열거형  (0) 2013.07.11
C# CMD 창 띄우지 않고 명령어 실행하기  (0) 2012.12.26
유니코드표  (0) 2012.09.24
C# Excel 범위로 데이터들 가져오기  (0) 2012.08.30

+ Recent posts