2015년 3월 16일 월요일

뇌자극 C# 5.0 - 21강

-네트워크 프로그래밍-

네트워크 프로그래밍은 전에 한번 열혈강의 시리즈로 공부한 적이 있다.
이 강에서는 파일 전송 프로그램을 하나 만드는 것으로 간단하게 기술하고 있다.

21.1 네트워크 프로그래밍에 앞서 알아둬야 할 기초


21.1.1 인터넷의 유래


네트워크 : 컴퓨터들이 상호 연결되어 있는 컴퓨터 통신 네트워크


21.1.2 TCP/IP 스택


컴퓨터끼리 네트워크에서 데이터를 주고받기 위해서는 
그 네트워크에서 통용되는 프로토콜을 따라야 한다
프로토콜은 규약, 규칙이라는 뜻의 낱말로 통신 규약을 뜻한다.

TCP/IP 는 사실상의 인터넷 표준 프로토콜이다.

TCP/IP 는 네 개의 계층으로 구성되어 있다.
(보통 7계층으로 설명하는데 여기선 4계층으로 설명한다….)

(1)   링크 계층
네트워크의 물리적인 연결 매체를 통해 패킷을 주고 받는 작업을 담당하는 부분
어떤 패킷이 네트워크를 통해 컴퓨터에 들어오면 물리적 데이터 전송에 사용되던 부분을 제거하고 인터넷 계층에 넘긴다.

(2)   인터넷 계층
패킷을 수신해야 할 상대의 주소를 지정하거나,
나가는 패킷에 대해서는 적절한 크기로 분할하고,
들어오는 패킷에 대해서는 재조립을 수행
이 계층에서 사용되는 규약이 IP .
IP는 패킷을 보내고 받기는 하지만 잘 가고 왔는지를 확인하지 않는다.
단지 인터넷 계층에서 주소를 지정하는 부분을 담당한다.

(3)   전송 계층
패킷의 운송을 담당하는 프로토콜들이 정의되어 있다.
TCP 는 전송 제어 프로토콜으로서 송신/수신간의 연결성을 제공하며, 신뢰성이 있는 패킷 전송 프로토콜이다.
TCP는 제대로 받고/보냈는지 확인하는 과정이 있어서 속도가 떨어지는 단점이 있다.
패킷이 빠르게 가야하고, 약간 손실되어도 크게 문제가 없는 경우에는
UDP를 사용한다.

(4)   애플리케이션 계층
각 응용프로그램의 나름의 프로토콜들이 정의되는 곳
웹 문서를 주고 받기 위한 HTTP, 파일 교환을 위한 FTP, 네트워크 관리를 위한 SNMP등이 애플리케이션 계층에서 정의된 프로토콜의 예다.

21.1.3 TCP/IP 주소 체계 : IP 주소


인터넷에서 패킷을 전달하려면 어디에서 보냈는지
어디로 보낼 것인지에 대한 정보가 필요하다.
이러한 정보를 “IP 주소라고 한다.

IP 주소체계는 부호 없는 8비트 정수 4개로 구성되어 있다.
8비트 정수는 0~255까지의 값을 가질 수 있으며
이 정수 네 개는 점(.)으로 연결하여 주소를 구성한다.

하지만 이 주소 체계도 전 세계적으로 쓰다보니 부족해졌다.
그래서 새로 생긴 주소 체계가 Iv6이다.
이 주소 체계는 128비트 길이의 주소 체계다.
IPv6 16비트의 수 8개를 콜론(:)으로 연결한다.

21.1.4 포트


큰 빌딩은 출입구가 여러 개 있듯이 같은 주소라도 입구가 여러 개인 경우가 많다.
IP 주소에도 출입문이 필요하다. 이 출입문을 포트(Port)라고 한다.

포트는 부호 없는 16비트 정수로써 0~65535 사이의 값을 이용한다.
보통 1~1024 까지는 전 세계적으로 합의된 값이 있는 포트를 사용함으로
개인적으로 사용할 것이면 이 값은 피하는 것이 좋다.

대표적인 포트 주소는 다음과 같다.

l  HTTP : 80
l  HTTPS : 443
l  FTP : 21
l  Telnet : 23
l  SMTP : 25
l  IRC : 194
l  IIOP : 535

21.1.5 TCP/IP 의 동작 과정


(1)   서버가 서비스를 시작, 클라이언트가 접속할 수 있도록 준비 하는 것
(2)   클라이언트는 서버에 접속을 시도 함
(3)   서버가 접속 시도를 수락하면 클라이언트와 데이터를 주고 받을 수 있게 됨
(4)   서버/클라이언트 간에 데이터 송/수신
(5)   클라이언트 접속 종료
(6)   서버 접속 종료

21. 2 TcpListener TcpClient


.NET 프레임워크가 TCP/IP 통신을 위해 제공하는 클래스다.
이 책에서는 Socket 클래스 대신 TcpListener / TcpClient 클래스를 이용한
네트워크 프로그래밍을 설명한다.
(Socket 클래스는 네트워크 프로그래밍 카테고리에 따로 적어뒀다)
서버와 클라이언트 각각이 갖고 있는 GetStream()이라는 메소드로 
NetworkStream 객체를 통해 데이터를 주고 받는다.

데이터를 보낼 때는 NetworkStream.Write( ),
데이터를 읽을 때는 NetworkStream.Read()를 호출한다.
서버/클라이언트 연결을 종료할 때는 Close() 메소드를 호출한다.

TcpListener / TcpClient 클래스의 주요 메소드

TcpListener 
-       -Start()  - 연결 요청 수신 대기를 시작한다.
-       -AcceptTcpClient() – 클라이언트의 연결 요청을 수락한다.
-       -Stop() – 연결 요청 수신 대기를 종료한다.

TcpClient
-       Connect() – 서버에 연결을 요청
-       Getstream() – 데이터를 주고 받는데 사용하는 매개체인 NetworkStream 을 가져옴
-       Close() – 연결을 닫는다.

21.3 흐르는 패킷


패킷을 요청한다고 바로 오는 것도 아니고 바로 가는 것도 아니다.
일정량의 버퍼에 담아놨다가 순차적으로 보낸다.
이유는 중간에 패킷이 손실되는 경우를 대비해서 일단은 버퍼에 저장해 놓았다가
손실되었을 경우에 재전송하기 위함이다.

21.3.1 프로토콜 설계와 네트워크 애플리케이션 프로그래밍 예제

TCP 네트워크 프로그래밍은 복잡하다.
내 컴퓨터에서 사용하던 객체를 바이트 스트림으로 바꿔 보내야 하고,
바이트 스트림으로 들어온 데이터를 다시 객체로 바꿔야 한다.

수신한 데이터가 정상인지도 검사해야 하고,
안정성을 위해 연결 상태도 수시로 점검해야 한다.

테스트를 할 때도 두 개의 프로그램을 띄워야 하고,
제대로 테스트 하려면 최소 두 대의 컴퓨터가 있어야 한다.
디버깅도 쉽지 않다.

이곳에 하나하나 기술하기에는 양도 너무 많고 이해하기도 힘들다.
역시 소스 코드를 다운 받아서 직접 실행해보는 것이 
가장 쉽게 터득하는 방법이 아닐까 싶다.

이 예제를 실행하려면 서버/클라이언트 프로그램을 각각 띄우고
자신의 컴퓨터의 IP와 상대 컴퓨터의 IP를 알아야 한다.


~ 드디어 뇌를 자극하는 C# 5.0 공부한 내용을 요약해서 블로그에 기재를 했다.
책으로 공부하고, 이곳에 요약하느라 또 한번 복습하게 되고
나중에 내가 필요하면 찾아보기도 위해서 이곳에 저장하는 것이다.
물론 다른 이들도 보면 좋겠지만 워낙 내 블로그는 접속하는 사람이 없으니
거의 다 나만 볼 것 같다 -_-;
당분간은 취업과 영어공부, 조직응용기사 실기에 집중 할 것이다.
그리고 만약 이곳에 기재를 한다면 디자인 패턴을 공부해 볼 생각이다.
그 다음은 리팩토리….;;
그 이후에는 작년에 사놓고 얼마 해보지도 못한 아두이노를 가지고 
이것저것 만들어 볼 예정이다.
회사를 다닌다고 해도 내 개인 프로젝트와 공부는 꾸준히 할 것이다.

요즘 허리가 많이 아퍼서 공부하는 것이 많이 느려졌지만 
꾸준한 노력만이 이 업계에서 살아 남는 길이라 생각한다.

뇌자극 C# 5.0 - 20강

-WinForm 으로 만드는 사용자 인터페이스-

20.1 도대체 무슨 일이 일어나고 있는 걸까?

WinForm은 폼 디자이너라는 툴로 프로그래머가 그림을 그리듯 UI를 만들 수 있게 해준다.
컨트롤은 윈도우 위에 배치할 때마다 폼 디자이너는 프로그램의 UI를 표시하는 한편
뒤로는 관련 C# 코드를 자동으로 만들어준다.
프로퍼티를 변경하거나, 이벤트 처리기를 추가할 때도 자동으로 코드를 수정해준다.
하지만 미세한 조정이 필요하거나 폼 디자이너에 문제가 생기는 경우 
직접 수정할 수 있어야 하기에 C# 코드로 WinForm UI를 만드는 법을 알아야 한다.

20.2 C# 코드로 WinForm 윈도우 만들기

Win32 API를 이용하여 윈도우 만드는 절차
(1)   윈도우 클래스를 정의한다
(2)   정의된 윈도우 클래스를 등록한다
(3)   윈도우를 생성한다
(4)   생성한 윈도우를 사용자에게 보여준다
(5)   메시지 루프를 돌면서 프로그램을 시작한다

WinForm 클래스를 이용한 윈도우 생성 절차
(1)   System.Windows.Forms.Form 클래스에서 파생된 윈도우 폼 클래스를 선언한다
(2)   (1)에서 만든 클래스의 인스턴스를 System.Windows.Form.Application.Run() 메소드에 매개 변수로 넘겨 호출한다.

20.3 Application 클래스

응용프로그램 시작 메소드 = Application.Run()
응용프로그램 종료 메소드 = Application.Exit()

Application 클래스는 응용프로그램이 받고 있는 수많은 메시지 중에
관심 있는 메시지만 걸러낼 수 있는 메시지 필터링 기능을 가지고 있다.

윈도우 운영체제에서 정의하고 있는 메시지는 식별번호(ID)가 붙여져 있다.
Application 클래스는 특정 ID를 갖는 메시지를 걸러내는 필터를 함께 등록해뒀다가
응용 프로그램에 메시지가 전달되면 해당 필터를 동작시킴

메시지의 ID가 필터에서 관심을 갖고 있는 값이라면 필터는 메시지를 요리하고,
그렇지 않다면 메시지를 거르지 않고 메시지를 받아야 하는 폼이나 컨트롤로 보내서
이벤트를 발생 시킨다.

20.4 윈도우를 표현하는 Form 클래스

여기서부터는 직접 해봐야 알 수 있는 것이기에 이 곳에 명시하기가 힘들다.
각종 컨트롤을 배치하고, 각 컨트롤에 속성을 바꾸고,
이벤트를 등록시켜서 동작하게 만드는 것이다.
예제를 다운받아서 직접 해보는 것이 가장 좋겠다.

책은 뇌를 자극하는 C# 5.0” 이다


2015년 3월 12일 목요일

뇌자극 C# 5.0 - 19 강

-쓰레드와 태스크-

19.1 프로세스와 스레드


프로세스 = 실행 파일이 실행되어 메모리에 적재된 인스턴스
스레드 = OS기 CPU 시간을 할당하는 기본 단위

프로세스가 밧줄이라면 스레드는 밧줄을 이루는 실이라고 할 수 있다.
프로세스는 최소한 한 개의 스레드를 가지고 있다

멀티 스레드의 장점과 단점


장점
(1) 사용자 대화형 프로그램에서 응답성을 높일 수 있다.
(2) 멀티 프로세스에 비해 멀티 스레드 방식이 자원 공유가 쉽다
(3) 이미 프로세스에 할당된 메모리와 자원을 그대로 사용해서 경제성이 좋다.

단점
(1) 구현하기가 매우 까다롭다.
(2) 테스트도 하기 힘들다
(3) 디버깅도 힘들다
(4) 자식 스레드 중 하나에 문제가 생기면 전체 프로세스가 문제가 생긴다.
(5) 너무 많이 사용하면 오히려 성능이 저하된다.

19.1.1 스레드 시작하기


(1)Thread 의 인스턴스를 생성한다. 생성자의 매개 변수로 스레드가 실행할 메소드를 매개 변수로 넘긴다

(2) Thread.start() 메소드를 호출하여 스레드를 시작한다

(3) Thread.Join() 메소드를 호출하여 스레드가 끝날 때까지 기다린다.

19.1.2 스레드 임의로 종료시키기


Thread 객체의 Abort() 메소드를 호출해주면 된다.
하지만 Abort() 메소드를 호출한다고 해서 동작하던 스레드가 즉시 종료된다는 보장이 없다.
예외가 있으면 예외처리까지 다 하고 나서야 종료가 되기 때문에 시간이 얼마나 걸릴지 모른다.
책에서는 Thread.Abort()를 사용하지 말라고 권고하고 있다(그럼 알려주질 말지 -_-)

19.1.3 스레드의 일생과 상태 변화


Unstarted - 스레드 객체를 생성한뒤 Start() 메소드가 호출되기 전 상태
Runing - 스레드가 시작하여 동작 중인 상태
Suspended - 스레드의 일시 중단 상태
WaitSleepJoin - 스레드가 블록(Block)된 상태
Aborted - 스레드가 취소된 상태
Stopped - 중지된 스레드의 상태
Background - 스레드가 백그라운드로 동작하고 있는 상태

Thread 객체의 ThreadState 필드를 통해 상태를 확인할 때는 반드시 비트 연산을 이용해야 한다.
ThreadState 열거형이 여러 상태를 동시에 나타낼 수 있도록 만들어져 있기 때문

19.1.4 스레드를 임의로 멈추는 또 하나의 방법 : 인터럽트


스레드를 강제로 종료시켜야 하는 경우에는
Thread.Interrupt()메소드를 사용하는 것이 안전하다

이유는 스레드의 상태가 WaitsleepJoin 상태면 바로 중지 시키지만
이 상태가 아닌 경우에는 스레드를 지켜보다가
WaitSleepJoin 상태가 되면 그때야 중단 시킨다

이런 특징 때문에 프로그래머는 최소한 코드가 "절대로 중단 되면 안 되는"
작업을 하고 있을 때 중단되지 않는 다는 보장을 받을 수 있다.

19.1.5 스레드 간의 동기화


동기화란? 스레드들이 순서를 갖춰 자원을 사용하게 하는 것을 "동기화"라고 한다.

스레드 동기화에서 가장 중요한 점은
"자원을 한번에 하나의 스레드가 사용하도록 보장"하는 것이다.

Lock 키워드로 동기화 하기 
예제)
class Counter
{
   public int count = 0;
   private readonly object thisLock = new object();

   public void Increase()
   {
      lock( thisLock )
      {
        count = count + 1;
      }
   }
}

//...
MyClass obj = new MyClass();
Thread t1 = new Thread(new ThreadStart(obj.DoSomething);
Thread t2 = new Thread(new ThreadStart(obj.DoSomething);
Thread t3 = new Thread(new ThreadStart(obj.DoSomething);

t1.Start();
t2.Start();
t3.Start();

t1.Join();
t2.Join();
t3.Join();

lock 키워드의 매개 변수로 사용하는 객체는 참조형이면 어느 것이든 쓸 수 있지만
외부 코드에서도 접근할 수 있는 다음 세가지는 절대 사용해선 안된다.

this : 클래스의 인스턴스는 클래스의 내부뿐만 아니라 외부에서 자주 사용된다.
Type 형식 : typeof 연산자나 object 클래스로부터 물려받은 GetType() 메소드는 Type 형식의 인스턴스를 반환한다.
string 형식 : 외부에서 접근 가능하기에 절대로 써선 안된다.

Monitor 클래스로 동기화 하기

Monitor 클래스는 스레드 동기화에 사용하는 몇 가지 정적 메소드를 제공한다

Monitor.Enter()/Monitor.Exit() 를 사용한 예제

public void Increase()
{
   int loopCount = 1000;
   while(loopCount--> 0)
   {
      Monitor.Enter(thisLock);
      try
      {
         count++;
      }
      finally
      {
         Monitor.Exit(thisLock);
      }
   }
}

Monitor.Wait()와 Monitor.Pulse()로 하는 저수준 동기화 예제

이 두 메소드는 반드시 look 블록 안에서 호출해야 한다.

(1) 클래스 안에 다음과 같이 동기화 객체 필드를 선언한다.
readonly object thisLock = new object();

(2) 스레드를 WaitSleepJoin 상태로 바꿔 블록시킬 조건을 결정할 필드를 선언
bool lockedCount = false;

(3) 스레드를 블록시키고 싶은 곳에서는 lock 블록 안에서 2번에서 선언한 필드를 검사하여 Monitor.Wait()를 호출한다.

lock(thisLock)
{
   while(lockedCount == true)
       Monitor.Wait(thisLock);
   //....
}

(4) 3번 과정에서 선언한 코드는 count가 0보다 크거나 lockedCount 가 true면 해당 스레드는 블록된다. 이렇게 블록되어 있던 스레드를 깨워나면 작업을 해야 한다.
가장 먼저 2번 과정에서 선언한 lockedCount의 값을 true로 변경한다.
이렇게 해두면 다른 스레드가 이 코드에 접근할 때 3번 과정에서 선언해둔 블로킹 코드에 걸려 같은 코드를 실행할 수 없게 된다.
작업을 마치면 lockedCount 의 값을 다시 false로 바꾼 뒤 Monitor.Pulse()를 호출한다.
그럼 Waiting Queue에 대기하고 있던 다른 스레드가 깨어나서 false로 바뀐 lockedCount를 보고 작업을 수행한다.

lock(thisLock)
{
   while(lockedCount == true)
      Monitor.Wait(thisLock);
   lockedCount = true;
   count++;
   lockedCount = false;
 
   Monitor.Pulse(thisLock);
}

19.2 Task와 Task<TResult>, 그리고 Parallel


비동기 코드를 작성할 수 있도록 하는 도구와 장치

19.2.1 System.Threading.Tasks.Task 클래스


동기 코드 = 메소드를 호출한 뒤에 실행이 완전히 종료되어야만 다음 메소드를 호출 가능

비동기 코드 = 메소드를 호출한 뒤에 메소드의 종료를 기다리지 않고 바로 다음 코드를 실행하는 것.

Task 클래스는 인스턴스를 생성할 때 Action 델리게이트를 넘겨받는다.
반환형을 갖지 않는 메소드와 익명 메소드, 무명 함수 등을 넘겨받는다.

(예제)
Action someAction= () =>
{
   Thread.Sleep(1000);
   Console.WriteLine("Printed asynchronously.");
};

Task myTask = new Task(someAction);
myTask.Start(); //생성자에서 넘겨받은 무명 함수를 비동기로 호출한다.

Console.WriteLine("Printed synchronously.");

myTask.Wait(); //myTask 비동기 호출이 완료될 때까지 기다린다.

19.2.2 코드의 비동기 실행 결과를 주는 Task<TResult> 클래스


(예제)
var myTask = Task<List<int>>.Run(
   ()=>
   {
      Tread.Sleep(1000);
      List<int> list = new List<int>();
      list.Add(3);
      list.Add(4);
      list.Add(5);
      return list;
   }
);

var myList = new List<int>();
myList.Add(0);
myList.Add(1);
myList.Add(2);

myTask.Wait();
myList.AddRange(myTask.Result.ToArray());

19.2.3 손쉬운 병렬 처리를 가능케 하는 Parallel 클래스


void SomeMethod(int i)
{
   Console.WriteLins(i);
}
//...
Parallel.For(0, 100, SomeMethod);

위 코드는 SomeMethod()를 병렬로 호출하면서
0~100 사이의 정수를 메소드의 매개변수로 넘긴다.

SomeMethod() 메소드를 병렬로 호출할 때
몇 개의 스레드를 사용할지는 Parallel 클래스가 내부적으로 판단하여 최적화 한다.

19.3 async 한정자와 await 연산자로 만드는 비동기 코드


async 한정자는 메소드, 이벤트 처리기, 테스크, 람다식 등을 수식함으로서
C# 컴파일러가 이들을 호출하는 코드를 만날 때
호출 결과를 기다리지 않고 바로 다음 코드로 이동하도록 실행 코드를 생성하게 한다.

(1)async로 한정하 void 형식 메소드는 await 연산자가 없어도 비동기로 실행된다.
(2)async로 한정한 Task 또는 Task<TResult>를 반환하는 메소드/태스크/람다식은
   await 연산자를 만나는 곳에 호출자에게 제어를 돌려주며,
   await 연산자가 없는 경우 동기로 실행된다.

2015년 3월 10일 화요일

뇌자극 C# 5.0 - 18 강

-파일 다루기-

18.1 파일 정보와 디렉토리 정보 다루기


파일 = 컴퓨터 저장 매체에 기록되는 데이터의 묶음
디렉토리 = 파일이 위치하는 주소

파일과 디렉토리 정보를 손쉽게 다루도록
System.IO 네임스페이스를 사용하고 다음과 같은 클래스를 제공한다

File = 파일의 생성, 복사, 삭제, 이동, 조회를 처리하는 정적 메소드를 제공한다
FileInfo = File 클래스와 하는 일은 동일하지만 정적 메소드 대신 인스턴스 메소드를 제공
Directory = 디렉토리의 생성,삭제,이동,조회를 처리하는 정적 메소드를 제공한다
DirectoryInfo = Directory 클래스와 하는 일은 동일하지만 인스턴스 메소드를 제공한다.

-File 클래스/ FileInfo 클래스 사용예제-

-File 클래스-

생성 - FileStream fs = File.Create("a.dat");
복사 - File.Copy("a.dat","b.dat");
삭제 - File.Delete("a.dat");
이동 - File.Move("a.dat", "b.dat");
존재 여부 확인 - if(File.Exists("a.dat"))
속성 조회 - Console.WriteLine(File.GetAttributes("a.dat"));

-FileInfo 클래스-

생성 - FlieInfo file = new FileInfo("a.dat");
        FileStream fs = File.Create();

복사 - FileInfo src = new FileInfo("a.dat");
        FileInfo dst = src.CopyTo("b.dat");

삭제 - FileInfo file = new FileInfo("a.dat");
        file.Delete();

이동 - FileInfo file = new FileInfo("a.dat");
        file.MoveTo("b.dat");

존재 여부 확인 - FileInfo file = new FileInfo("a.dat");
                    if(file.Exists())

속성 조회 - FileInfo file = new FileInfo("a.dat");
              Console.WriteLine(file.Attributes);

-Directory/DirectoryInfo 클래스 사용예제-

-Directory 클래스-

생성 - Directory dir = Directory.CreateDirectory("a");
삭제 - Directory.Delete("a");
이동 - Directory.Move("a","b");
존재 여부 확인 - if(Directory.Exists("a.dat");
속성 조회 - Console.WriteLine(Directory.GetAttributes("a");
하위 디렉토리 조회 -string[] dirs = Directory.GetDirectories("a");
하위 파일 조회 - string[] files = Directory.GetFiles("a");

-DirectoryInfo 클래스-

생성 - DirectoryInfo dir = new DirectoryInfo("a");
        dir.Create();
삭제 - DirectoryInfo dir = new DirectoryInfo("a");
        dir.Delete();
이동 - DirectoryInfo dir = new DirectoryInfo("a");
        dir.MoveTo("b");
존재 여부 확인 - Directory dir = new DirectoryInfo("a");
                   if(dir.Exists)
속성 조회 - DirectoryInfo dir  = new DirectoryInfo("a");
             Console.WriteLine(dir.Attributes);
하위 디렉토리 조회 - DirectoryInfo dir = new DirectoryInfo("a");
                        DirectoryInfo[] dirs = dir.GetDirectories();
하위 파일 조회 - DirectoryInfo dir = new DirectoryInfo("a");
                   FileInfo[] files = dir.GetFiles();

18.2 파일을 읽고 쓰기 위해 알아야 할 것들


먼저 스트림의 개념을 알아야 한다.
스트림은 데이터가 흐르는 통로를 뜻한다.
메미로에서 하드디스크로 데이터를 옮길 때(파일 쓰기)는
먼저 스트림을 만들어 둘 사이를 연결하고
메모리에 있는 데이터를 바이트 단위로 하드디스크로 옮겨 넣는다.

스트림은 데이터의 흐름이기 때문에 스트림을 이용하여
파일을 다룰 때는 처음부터 끝까지
순서대로 읽고 쓰는 것이 보통이다.(이것을 순차 접근 방식이라고 한다).

하지만 하드디스크는 암과 헤드를 움직어
디스크의 어떤 위치에 기록된 데이터라도 즉시, 찾아갈 수 있다.
이렇게 임의의 주소에 있는 데이터에 접근하는 것을 임의 접근 방식이라고 한다.

스트림을 쓰기 위해서는 System.IO.Stream 클래스를 사용해야 한다.
스트림 클래스는 그 자체로 입력/출력 스트림 역할을 모두 할 수 있고,
순차/임의 접근 방식 모두를 지원한다.

단, 스트림 클래스는 추상 클래스이기 때문에 인스턴스를 직접 만들어 사용할 수는 없고,
이 클래스로부터 파생된 클래스를 이용해야 한다.

파일을 쓰는데는 다음과 같은 과정이 필요하다

1) 파일 스트림 생성
2) 값을 byte 배열로 변환
3) 변환한 byte 배열을 파일 스트림을 통해 파일에 기록
4) 파일 스트림 닫기

파일 읽는 과정도 비슷하다

1) 파일 스트림 생성
2) 값의 길이 만큼 데이터를 읽어 변수에 저장
3) BitConverter를 이용하여 변수에 담겨 있는 값을 Long 형으로 변환
4) 파일 스트림 닫기

18.3 이진 데이터 처리를 위한 BinaryWriter/BinaryReader


FileStream 클래스는 파일 처리를 위한 모든 것을 가지고는 있지만 사용하기가 불편하다.
데이터 저장할 때는 반드시 byte 형식 또는 byte의 배열 형식으로
변환해야 한다는 문제가 있다.
파일로부터 데이터를 읽을 때도 마찬가지다.
이런 불편함을 해소하기 위해 BinaryWriter/BinaryReader 가 만들어졌다.

(예제 - BinaryWriter)

BinaryWriter bw = new BinaryWriter( new FileStream("a.dat", FileMode.Create) );

bw.Write(32);
bw.Write("Good Morning");
bw.Write(3.14);

bw.Close();

1)BinaryWriter 의 생성자를 호출 하면서 FileStream 의 인스턴스를 매개 변수로 넘긴다.
2)인스턴스에서 Write() 메소드를 호출하고 데이터를 넣는다.
3)스트림을 닫아준다.

(예제 - BinaryReader)

BinaryReader br = new BinaryReader( new FileStream("a.dat", FileMode.Open) );

int a = br.ReadInt32();
string b = br.ReadString();
double c = br.ReadDoble();
br.Close();

1) BinaryReader 의 생성자를 호출하면서 FileStream 의 인스턴스를 매개 변수로 넘긴다.
2) BinaryReader는 읽을 데이터의 형식별로 Readxxx() 메소드(xxx는 읽을 데이터 형식의 이름)를 제공한다.
3) 스트림을 닫는다.

18.4 텍스트 파일 처리를 위한 StreamWriter/StreamReader


텍스트 파일 처리 역시 비슷하다.

(예제 - StreamWriter)

StreamWriter sw = new StreamWriter( new FileStream("a.dat", FileMode.Create));

sw.Write(32);
sw.WriteLine("Good Morning!");
sw.WriteLine(3.14);

sw.Close();

(예제 - StreamReader)

StreamReader sr = new StreamReader( new FileStream("a.dat", FileMode.Open) );

while(sr.EndOfStream == false)
{
    Console.WriteLine(sr.ReadLine());
}

sr.Close();

18.5 객체 직렬화하기


BinaryWriter/Reader 와 StreamWriter/Reader 은
기본 데이터 형식을 스트림에 쓰고 읽을 수는 있지만

프로그래머가 직접 정의한 클래스나 구조체 같은
복합 데이터 형식은 지원하지 않는다.

복합 데이터 형식을 쉽게 스트림에 읽기/쓰기를 할 수 있게 하는 것이 직렬화 이다.

직렬화란?
객체의 상태를 메모리나 저장 장치에 저장 가능한 0과 1의 순서로 바꾸는 것이다.
C#에서는 객체를 직렬화 하는 방법이 쉽다.
그저 [Serializable]를 클래스 선언부 앞에 붙여주면,
그 클래스는 메모리나 저장장치에 저장이 가능한 형식이 된다.

(예제 - 직렬화)

[Serializable]
class MyClass
{
   //...
}

Stream ws = new FileStream("a.dat", FileMode.Create);

//BinaryFormatter는 객체를 직렬화 하거나 역직렬화 하는 클래스
BinaryFormatter serializer = new BinaryFormatter();

MyClass obj = new MyClass();

serializer.Serialize(ws, obj); //직렬화
ws.Close();

(예제 - 역직렬화)

Stream rs = new FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();

MyClass obj = (MyClass)deserializer.Deserialize(rs);  //역직렬화
rs.Close();

만약 직렬화 하고 싶지 않은 필드가 있다면
그 필드에만 NonSerialzed 애트리뷰트로 수식하면 된다.
이렇게 하면 필드의 상태는 직렬화 할 때도 제외되고, 역직렬화 할 때도 복원되지 않는다.

(예제 - NonSerialzed)

[Serializable]
class MyClass
{
   public int myField1;
   public int myField2;
 
   [NonSerialized]
   pulblic int myField3; //이 필드만 직렬화 되지 않는다.

   public int myField4; //이 필드는 직렬화 된다
}


뇌자극 C# 5.0 - 17강

-dynamic 형식-


17.1 dynamic 형식 소개


dynamic 형식도 int, string처럼 또 하나의 데이터 형식이다.
형식 검사가 컴파일할 때 이루어지는 다른 형식과는 달리
실행할 때 이루어진다는 점이 dynamic 형식의 특징이다.

class MyClass
{
   public void FuncAAA()
   {   어쩌구 저쩌구    }
}

class MainApp
{
   static void Main(string[] args)
   {
      MyClass obj = new MyClass();
      obj.FuncAAA();
      obj.FuncBBB();    // 이부분이 에러가 남
   }
}

위의 코드는 컴파일 자체가 되질 않는다.
없는 메소드를 선언 했으니 당연히 에러가 난다. 하지만 dynamic 형식을 쓰면...

class MainApp
{
   static void Main(string[] args)
   {
      dynamic obj = new MyClass();
      obj.FuncAAA();
      obj.FuncBBB(); //dynamic 형식으로 선언된 obj는 일단 컴파일 형식 검사는 피함
   }
}

형식 검사를 컴파일 할 때에서 실행할 때로 미루겠다는 것이지, 하지 않는 것은 아니다.

17.2 COM 과 .NET 사이의 상호 운용성을 위한 dynamin 형식


COM 이란?

마이크로소프트의 소프트웨어 컴포넌트 규격을 말한다.

하지만 닷넷이 나오면서 COM과 C#이 서로 문제가 생겼다.

(1) COM은 메소드가 결과를 반환할 때 실제 형식이 아닌 object 형식으로 반환한다.
(2) COM은 오버로딩을 지원하지 않는다. 대신 메소드와 선택적 매개 변수와 기본값 매개 변수를 지원한다.

이 두가지를 C# 4.0에서 dynamic 형식의 도입을 통해 (1)을 해결하고,
메소드의 선택적 매개 변수와 기본 값 매개 변수 도입을 통해 (2)문제를 해결했다.

뇌자극 C# 5.0 - 16강

-리플렉션과 애트리뷰트-

16.1 리플렉션

객체를 엑스레이처럼 객체의 형식 정보를 들여다보는 기능이다.
이 기능을 이용하면 프로그램 실행 중에
객체의 형식 이름, 프로퍼티 목록, 필드, 이벤트 목록까지 모두 열어 볼 수 있다.

16.1.1 Object.GetType() 메소드와 Type 클래스


Object는 모든 데이터 형식의 조상이다.
모든 데이터 형식은 Object 형식이 갖고 있는 다음 메소드를 물려받아 갖고 있다는 뜻
Equals(), GetHashCode(), GetType(), ReferenceEquals(), ToString()

위 다섯개 메소드 중 GetType()이 객체의 형식 정보를 반환하는 기능을 한다.
GetType() 메소드는 Type형식의 결과를 반환한다.
Type형식은 닷넷에서 사용되는 데이터 형식의 모든 정보를 담고 있다.

메소드                        반환형식             설명
GetConstructors()           ConstructorInfo()   모든 생성자 목록을 반환한다
GetEvents()                  EventInfo()          이벤트 목록을 반환한다
GetFields()                   FieldInfo()           필드 목록을 반환한다
GetGenericArguments()     Type()               매개 변수 목록을 반환한다
GetInterfaces()              Type()               인터페이스 목록을 반환한다
GetMembers()               MemberInfo()       멤버 목록을 반환한다
GetMethods()                MethodInfo()       메소드 목록을 반환한다
GetNestedTypes()           Type()               내장 형식 목록을 반환한다
GetProperties()              PropertyInfo()       프로퍼티 목록을 반환한다

16.1.2 리플렉션을 이용해서 객체 생성하고 이용하기


리플렉션을 이용해서 동적으로 인스턴스를 만들기 위해서는
System.Activator 클래스의 도움이 필요하다

인스턴스를 만들고자 하는 형식의 Type 객체를 매개 변수로 넘기면,
Activator.CreateInstance() 메소드는 입력받은 형식의 인스턴스를 생성하여 반환한다.

object a = Activator.CreateInstance(typeof(int));

일반화를 지원하는 버전의 CreateInstance()메소드도 있다.

List<int> list = Activator.CreateInstance<List<int>>();

16.1.3 형식 내보내기


동적으로 새로운 형식을 만드는 작업은
System.Reflection.Emit 네임스페이스에 있는 클래스를 통해서 이루어진다.

이 클래스를 쓰는 요령은 다음 순서와 같다

1)AssemblyBuilder 을 이용해서 동적으로 어셈블리를 만든다.

2)ModuleBuilder 를 이용해서 1)에서 생성한 어셈블리 안에 모듈을 만들어 넣는다.

3)TypeBuilder 를 2)에서 생성한 모듈 안에 클래스(형식)을 만들어 넣는다.

4) 3)을 생성한 클래스 안에 메소드나 프로퍼티를 만들어 넣는다.

5) 4)에서 생성한 것이 메소드라면,
   ILGenerator를 이용해서 메소드 안에 CPU가 실행할 IL 명령들을 넣는다.

16.2 애트리뷰트

애트리뷰트(Attribute)는 코드에 대한 부가 정보를 기록하고 읽을 수 있는 기능이다.
애프트뷰트가 주석과 다른 점은 주석은 사람이 읽고 쓰는 정보라면,
애트리뷰트는 사람이 작성하고 컴퓨터가 읽는다는 것이다.

애트리뷰트를 이용해서 클래스나 구조체, 메솓, 프로퍼티 등에 데이터를 기록해두면 
이 정보를 C# 컴파일러나 C#으로 작성된 프로그램이 이 정보를 읽어서 사용할 수 있다.

애트리뷰트나 리플렉션을 통해 얻는 정보들을 메타데이터라고 한다.

16.2.1 애트리뷰트 사용하기


[애트리뷰트_이름( 애트리뷰트_매개_변수 )]
public void MyMethod()
{
    //....
}

예제)
class MyClass
{
   [Obsolete("OldMethod는 폐기 되었습니다. NewMethod()를 이용하세요.")]
   public void OldMethod()
   {
      Console.WriteLine("옛날 버전");
   }
   public void NewMethod()
   {
      Console.WriteLine("최신 버전");
   }
}

예제를 OldMethod()를 사용하고 컴파일 하면 경고 메세지가 IDE에 나타나게 된다.
자연스럽게 최신버전을 쓰게 프로그래머에게 알려줄 수 있다.

16.2.2 호출자 정보 애트리뷰트


C#5.0 버전부터 호출자 정보라는 애트리뷰트가 도입되었다.
호출자 정보는 메소드의 매개 변수에 사용되며, 
메소드의 호출자 이름, 호출자 메소드가 정의 되어 있는 소스 파일 경로, 
소스 파일 내의 행 번호를 알 수 있다.

응용프로그램의 이벤트를 로그 파일이나 화면에 출력하면 
그 이벤트가 어떤 코드에서 일어 났는지 알 수 있다.

3가지 호출자 정보 애트리뷰트를 설명한다.

CallerMemberNameAttribute : 현재 메소드를 호출한 메소드 또는 프로퍼티의 이름을 나타냄

CallerFilePathAttribute : 현재 메소드가 호출된 소스 파일 경로를 나타낸다. 이때 경로는 소소 코드를 컴파일 할 때의 전체 경로를 나타낸다.

CallerLineNumberAttribute : 현재 메소드가 호출된 소스 파일 내의 행 번호를 나타낸다.


2015년 3월 9일 월요일

뇌자극 C# 5.0 - 15강

-LINQ-


15.1 데이터!!데이터!!데이터!!


LINQ는 C# 언어에 통합된 데이터 질의 기능이다.
질의란 데이터에 대해 물어본다는 말이다.
기본적인 질문은 다음과 같다

From : 어떤 데이터 집합에서 찾을 것인가?
Where : 어떤 값의 데이터를 찾을 것인가?
Select : 어떤 항목을 추출할 것인가?

List<Profile> profiles = new List<Profile>();
foreach (Profile profile in arrProfile)
{
    if(profile.Height < 175)
         profiles.Add(profile);
}
profiles.Sort(
     (profile1, profile2) =>
     {
        return profile1.Height - profile2.Height;
     }
);
foreach(var profile in profiles)
    Console.WriteLine("{0}, {1}", profile.Name, profile.Height);

위의 코드를 LINQ 로 바꾸면 아래와 같이 된다.

var profiles = from profile in arrProfile //arrProfile 안에 있는 각 데이터로부터
               where profile.Height < 175  //Height가 175 미만인 객체만 골라
               orderby profile.Height   //키순으로 정렬하여
               select profile;             //profile 객체를 추출한다.
foreach (var profile in profiles)
     Console.WriteLine("{0}, {1}", profile.Name, profile.Height);

15.2 LINQ의 기본 : from, where, orderby, select


15.2.1 from

모든 LINQ 쿼리식은 반드시 from 절로 시작한다.
쿼리식의 대상이 될 데이터 원본과 데이터 원본 안에 들어 있는 
각 요소 데이터를 나타내는 범위 변수를 from절에서 지정해 줘야 한다.
이 때 from의 데이터 원본은 아무 형식이나 사용할 수 없고,
IEnumerable<T> 인터페이스를 상속하는 형식이어야 한다.

배열이나 컬렉션 객체들은 IEnumerable<T>를 상속하기 때문에 
이들은 모두 from 절의 데이터 원본으로 사용할수 있다.

from 절을 이용해서 데이터 원본으로 부터 범위 변수를 뽑아낸 후에,
LINQ가 제공하는 수십가지 연산을 이용해서 데이터를 가공 및 추출할 수 있다.

15.2.2 where

where는 필터 역할을 하는 연산자다.
from이 데이터 원본으로부터 뽑아내면 
where 연산자로 조건을 걸어 부합하는 데이터만 걸러낸다.

var profiles = from profile in arrProfile //arrProfile 안에 있는 각 데이터로부터
               where profile.Height < 175  //Height가 175 미만인 객체만 골라
               orderby profile.Height   //키순으로 정렬하여
               select profile;             //profile 객체를 추출한다.

15.2.3 orderby


orderby 는 데이터의 정렬을 수행하는 연산자다.
기본적으로 오름차순으로 데이터를 정렬하지만,
오름차순으로 명시하기 위해서는 ascending 키워드로 명시하면 된다.

var profiles = from profile in arrProfile
               where profile.Height < 175
               orderby profile.Height ascending   //키순 오름차순으로 정렬
               select profile;          

내림차순으로 명시하기 위해서는 descending 키워드로 명시하면 된다

var profiles = from profile in arrProfile
               where profile.Height < 175
               orderby profile.Height  descending //키순 내림차순으로 정렬
               select profile;          

15.2.4 select

select 절은 최종 결과를 추출하는 쿼리식이다.

var profiles = from profile in arrProfile
               where profile.Height < 175
               orderby profile.Height
               select profile.Name;

위와 같이 Name만 추출하면 profiles 에는 string 형식으로 컴파일된다.
또한, select 문은 무명형식을 이용해서 다음과 같이 새로운 형식을 즉성에서 만들수 있다.

var profiles = from profile in arrProfile
               where profile.Height < 175
               orderby profile.Height
               select new {Name=profile, name, InchHeight=profile.Height*0.393};

15.3 여러 개의 데이터 원본에 질의하기


LINQ 쿼리식은 데이터 원본에 접근하기 위해 from절을 사용한다
여러개의 데이터 원본에 접근하려면 from문을 중첩해서 사용하면 된다.

예)

var classes = from c in arrClass    //첫번째 데이터 원본
                  from s in c.Score  //두번째 데이터 원본
                  where s < 60
               select new {c.Name, Lowest = s}; 


15.4 group by 로 데이터 분류하기


데이터를 그룹화 해주는 질의문이다.

group A by B into C

A에는 from 절에서 뽑아낸 범위 변수를, 
B에는 분류 기준을, 
C에는 그룹 변수를 위치 시키면 된다.

예)
var listProfile = from profile in arrProfile
                 group profile by profile.Height < 175 into g
                 select new {GroupKey = g.key, Profiles = 0};

15.5 두 데이터 원본을 연결하는 join


join 은 두 데이터 원본을 연결하는 연산이다.
각 데이터 원본에서 특정 필드의 값을 비교하여 
일치하는 데이터 끼리 연결을 수행한다.

15.5.1 내부조인


내부조인은 교집합과 비슷하다. 
두 데이터 원본 사이에서 일치하는 데이터들만 연결한 후 반환한다.
기준 데이터 a는 from절에서 뽑아낸 범위 변수이고, 
연결 대상 데이터 b는 join 절에서 뽑아낸 변수다. 
join 절의 on 키워드조인 조건을 수반한다
on 절의 조인 조건은 동등만 허용한다.
동등은 == 대신 equals  키워드를 사용한다.

from a in A
join b in B on a.xxx equals b.yyy

15.5.2 외부 조인


외부 조인은 기본적으로 내부 조인과 비슷하지만, 
기준이 되는 데이터 원본은 모두 다 포함된다는 점이 다르다.

예제)

var listProfile = 
   from profile in arrProfile
   join product in arrProduct on profile.Name equals product.Star into ps
   from product in ps.DefaultIfEmpty(new Product(){Title="그런거 없음"})
   select new
   {
      Name = profile.Name,
      Work = product.Title,
      Height = profile.Height
   };

15.6 LINQ의 비밀과 LINQ 표준 연산자


LINQ에는 많은 연산자가 있으나 여기에 다 쓰기엔 너무 힘들다 -_-;
그래서 msdn를 찾아볼 것을 추천한다.

뇌자극 C# 5.0 - 14강

-람다식-

14.1 처음 만드는 람다식

람다식은 익명 메소드를 만들기 위해 사용한다.
람다식으로 만드는 익명 메소드는 무명 함수라는 이름으로 부른다.

람다식 선언하는 형식

매개변수목록 => 식

=>는 입력 연산자 이다. 이 연산자의 역할은 그저 매개 변수를 전달하는 것 뿐이다.
람다식에서는 =>를 중심으로 왼편에는 매개변수가, 오른편에는 식이 위치한다.

예제)
//익명 메소드를 만들려면 델리게이트가 필요하다
delegate int Calculate(int a, int b);
...
...
static void Main(string[] atgs)
{
   Calculate calc = (int a, int b) => a + b; //익명 메소드를 람다식으로 만든 것
}

C#컴파일러는 위 코드를 한층 더 간결하게 만들 수 있도록 "형식유추"라는 기능을 제공한다.

static void Main(string[] atgs)
{
   Calculate calc = (a, b) => a + b; //익명 메소드를 람다식으로 만든 것
}

14.2 문 형식의 람다식

문 형식의 람다식은 => 연산자의 오른편에 식 대신 {와 }로 둘려싸인 코드블록이 위치한다.
형식은 다음과 같다.

(매개_변수_목록) => {
                           문장1;
                           문장2;
                           ....
                        }

문형식의 람다식에는 반환형식이 없는 무명함수를 만들 수 있다.

delegate void DoSomething();
//....
static void Main(string[] args)
{
   DoSomething DoIt = (  ) =>
          {
              Console.WriteLine("뭔가를");
              Console.WriteLine("출력해보자.");
              Console.WriteLine("이렇게!");
          }; //문장 형식의 람다식은 {}로 둘러싼다.
   DoIt();
}

14.3 Func와 Action으로 더 간편하게 무명 함수 만들기


익명 메소드와 무명 함수는 코드를 더 간결하게 만들어주는 요소다.
Func 델리게이트는 결과를 반환하는 메소드를 참조하기 위해
Action 델리게이트는 결과를 반환하지 않는 메소드를 참조한다.

Func 델리게이트의 형식 매개 변수 중 가장 마지막에 있는 것이 반환 형식이다.

예제 - 입력 매개 변수가 없는 버전)

Func<int> func1 = () => 10;  //입력 매개변수는 없으며, 무조건 10을 반환
Console.WriteLine(func1());  //10 출력

예제 - 입력 매개 변수가 하나 있는 버전)

Func<int, int> func2 = (x) => x*2;  //입력 매개변수는 int 형식 하나, 반환 형식도 int
Console.WriteLine(func2(3));  //6 출력

예제 - 입력 매개 변수가 두 개 있는 버전)

//입력 매개 변수는 int형식 둘, 반환 형식은 int
Func<int, int, int> func3 = (x, y) => x+y;  
Console.WriteLine(func3(2, 3));  //5 출력

Action 델리게이트

Action 델리게이트는 Func 델리게이트와 거의 같다.
차이점은 Action 델리게이트는 반환 형식이 없다는 것 뿐이다.

Action 델리게이트의 형식 매개 변수는 모두 입력 매개 변수를 위해 선언되어 있다.

예제-매개 변수가 아무 것도 없는 Action 사용)

Action act1 = () => Console.WriteLine("Action()");
act1(); 

예제 - 매개 변수가 하나뿐인 버전)

int result = 0;

//람다식 밖에서 선언한 result에 x*x의 결과를 저장
Action<int> act2 = (x) => result = x * x; 

act2(3);
Console.WriteLine("result : {0}", result); //9 를 출력

예제 - 매개 변수가 두 개인 Action<T1, T2> 델리게이트의 사용)

Action<double, double> act3 = (x,y) =>
{
   double pi = x/y;
   Console.WriteLine("Action<T1, T2>({0}, {1}) :{2}", x,y,pi);
};

act3 (22.0, 7.0);

뇌자극 C# 5.0 - 13강

-델리게이트와 이벤트-

13.1 델리게이트란?


비서처럼 어떤 일을 해줄 코드를 두고,
이 코드가 실행할 세부 코드는 컴파일 시점이 아닌
실행 시점에 부여하는 것을 콜백이라고 부르는데

델리게이트는 이 콜백을 구현하기 위해 사용된다.
델리게이트는 메소드에 대한 참조다.

한정자 delegate 반환형식 델리게이트이름 ( 매개변수목록 );

델리게이트는 인스턴스가 아닌 형식이다.

델리게이트를 이용하여 콜백을 구현하는 과정

1)델리게이트를 선언한다
2)델리게이트의 인스턴스를 생성한다. 인스턴스를 생성할 때는 델리게이트가 참조할 메소드를 매개 변수로 넘긴다.
3)델리게이트를 호출한다.

13.2 델리게이트는 왜, 그리고 언제 사용하나요?

프로그래밍을 하다 보면 "값"이 아닌 "코드" 자체를 매개 변수로 넘기고 싶을 때
델리게이트를 쓴다.

자세히 알아보자.

1)델리게이트를 선언한다.

delegate int Compare(int a, int b);

2) Comparer 델리게이트가 참조할 비교 메소드를 작성한다.

static int AscendComparer(int a, int b)
{
     if(a>b)
        return 1;
     else if(a==b)
        return 0;
     else
        return -1;
}

3) 정렬할 배열과 비교 메소드를 참조할 델리게이트를 매개 변수로 받는 정렬 메소드를 작성한다

static void BubbleSort(int[] DataSet, Compare Comparer)
{
   int i = 0;
   int j = 0;
   int temp = 0;

   for(i = 0; i<DataSet.Length - (i+1); i++)
   {
      if(Comparer(DataSet[j], DataSet[j+1])>0
      {
         temp = DataSet[j+1];
         DataSet[j+1] = DataSet[j];
         DataSet[j] = temp;
      }
   }
}

4) 정렬 메소드를 호출하면 원하는 대로 정렬 방식이 분리된 정렬 코드를 얻을 수 있다.

int[] array = {3,7,4,2,10};
BubbleSort(array, new Compare(AscendComparer); //array는 {2,3,4,7,10}

13.3 일반화 델리게이트

델리게이트는 보통의 메소드뿐 아니라 일반화 메소드도 참조할 수 있다.
이 경우에는 델리게이트도 일반화 메소드를 참조할 수 있도록 
형식 매개 변수를 이용하여 선언해야 한다.

delegate int Compare<T>(T a, T b);

예) 위의 함수를 일반화로 바꾼다면...

static void BubbleSort<T>(T[] DataSet, Compare<T> Comparer)
{
   int i = 0;
   int j = 0;
   int temp = 0;

   for(i = 0; i<DataSet.Length - (i+1); i++)
   {
      if(Comparer(DataSet[j], DataSet[j+1])>0
      {
         temp = DataSet[j+1];
         DataSet[j+1] = DataSet[j];
         DataSet[j] = temp;
      }
   }
}

13.4 델리게이트 체인

델리게이트 하나가 여러개의 메소드를 동시에 참조할 수 있다.

예제)

delegate void ThereIsAFire(string location);

void Call119(string location)
{
   Console.WriteLine("소방서죠? 불났어요! 주소는 {0}", location);
}

void ShotOut(string location)
{
   Console.WriteLine("피하세요!! {0}에 불이 났어요!", location);
}

void Escape(string location)
{
   Console.WriteLine("{0}에서 나갑시다!", location);
}


이렇게 선언한 메소드들은 델리게이트의 인스턴스가 자신들을 동시에 참조할 수 있도록 
+= 연산자를 이용하여 결합할 수 있다.

ThereIsAFire Fire = new ThereIsAFirs(Call119);
Fire += new ThereIsAFire(ShotOut);
Fire += new ThereIsAFire(Escape);

Fire("우리집");

-출력결과-
소방서죠?불났어요!주소는 우리집
피하세요! 우리집에 불이 났어요!
우리집에서 나갑시다!

13.5 익명 메소드

익명 메소드는 이름 그대로 이름이 없는 메소드다.
이름이 없는 메소드를 델리게이트에서 사용하는 법을 알아본다

delegate int Calculate(int a, int b);

라는 델리게이트가 있다면 익명 메소드로는 아래와 같이 쓴다.

public static void Main()
{
   Calculate Calc;
   calc = delegate(int a, int b)
           {
               return a + b;
           }
   Console.WriteLine("3+4 : {0}", Calc(3,4) );
}

익명 메소드는 delegate 키워드를 이용하여 선언한다.

델리게이트 인스턴스 = delegate (매개변수목록)
                          {
                             //실행하고자 하는 코드....
                          }

13.6 이벤트 : 객체에 일어난 사건 알리기

이벤트를 선언하고 사용하는 절차

1)델리게이트를 선언

delegate void EventHandler(string message);

2)클래스 내에 1)에서 선언한 델리게이트 인스턴스를 event 한정자로 수식해서 선언 한다

class MyNotifier
{
   public event EventHandler SomethingHappened;
   public void DoSomething(int number)
   {
      int temp = number % 10;
      if(temp != 0 && temp % 3== 0)
      {
          SomethingHappened(String.Format("{0}: 짝", number);
      }
   }
}

3)이벤트 핸들러 작성. 이벤트 핸들러는 1)에서 선언한 델리게이트와 일치하는 메소드면 된다.

class MainApp
{
    static public void MyHandler(string message)
    {
       Console.WriteLine(message);
    }
    //....
}

4)클래스의 인스턴스를 생성하고, 이 객체의 이벤트에 3)에서 작성한 이벤트 핸들러를 등록

class MainApp
{
   static public void MyHandler(string message)
   {
      Console.WriteLine(message);
   }
   static void Main(string[] args)
   {
     MyNotifer notifier = new MyNotifier();
     notifier.SomethingHappened += new EventHandler(MyHandler);
     for(int i = 1; i < 30; i++)
     {
         notifier.DoSomething(i);
     }
   }
}

5)이벤트가 발생하면 이벤트 핸들러가 호출된다.

class MainApp
{
   static public void MyHandler(string message)
   {
      Console.WriteLine(message);
   }
   static void Main(string[] args)
   {
     MyNotifer notifier = new MyNotifier();
     notifier.SomethingHappened += new EventHandler(MyHandler);
     for(int i = 1; i < 30; i++)
     {
         notifier.DoSomething(i);
     }
   }
}

13.7 델리게이트와 이벤트

이벤트는 델리게이트에 event 키워드로 수식해서 선언한 것에 불과하다.
이벤트가 델리게이트와 가장 크게 다른 점은 
이벤트는 외부에서 직접 사용할 수 없다는 것이다.
이벤트는 public 로 선언되어 있어도 
자신이 선언되어 있는 클래스 외부에서는 호출이 불가능 하다

델리게이트는 콜백 용도로 사용하고,
이벤트는 객체의 상태 변화사건의 발생을 알리는 용도로 구분해서 사용해야 한다.

2015년 3월 8일 일요일

뇌자극 C# 5.0 - 11~12강

-일반화 프로그래밍-


11.1 일반화 프로그래밍이란?


특수한 개념으로부터 공통된 개념을 찾아 묶는 것을 "일반화"라고 한다.
일반화 프로그래밍은 데이터 형식을 묶는 것.
기능은 같으나 데이터 형식만 다른 경우 일반화를 시켜서 하나로 묶어서 구현하는 것

11.2 일반화 메소드


일반화 메소드는 이름처럼 데이터 형식을 일반화한 메소드이다.
일반화할 형식이 들어갈 자리에 구체적인 형식의 이름 대신 형식 매개변수가 들어간다.

한정자 반환형식 메소드이름<형식매개 변수> (매개변수목록)
{
  //...
}

예제)

//int 버전
void CopyArray( int[] source, int[] target )
{
   for(int i = 0; i < source.Length; i++ )
        target[i] = source[i];
}

//string 버전
void CopyArray( string[] source, string[] target )
{
   for(int i = 0; i < source.Length; i++ )
        target[i] = source[i];
}

를 아래와 같이 일반화 할 수 있다.

void CopyArray<T> ( T[] source, T[] target )
{
   for(int i = 0; i < source.Length; i++ )
        target[i] = source[i];
}

11.3 일반화 클래스


일반화 클래스는 데이터 형식을 일반화한 클래스.

class 클래스 이름 <형식매개 변수>
{
    //....
}

예제)
class Array_Int
{
    private int[] array;
    //...
    public int GetElement(int index){ return array[index]; }
}

class Array_Double
{
    private double[] array;
    //...
    public double GetElement(int index){ return array[index]; }
}

를 아래와 같이 일반화 할 수 있다.

class Array_Generic< T >
{
    private T[] array;
    //...
    public T GetElement(int index){ return array[index]; }
}

Array_Generic 클래스 사용하는 법

Array_Generic<int> intArr = new Array_Generic<int>();
Array_Generic<double> dbArr = new Array_Generic<double>();

11.4 형식 매개 변수 제약시키기


일반화 메소드나 일반화 클래스가 입력받는
형식 매개 변수 T는 "모든"데이터 형식을 대신할 수 있다.
특정 조건을 갖춘 형식에만 대응하는 형식 매개 변수가 필요할 때
형식 매개 변수의 조건에 제약을 줄 수 있다.

where 형식매개 변수 : 제약조건

where T : struct  => T는 값 형식이어야 한다.
where T : class  => T는 참조 형식이어야 한다.
where T : new() => T는 반드시 매개 변수가 없는 생성자가 있어야 한다.
where T : 기반_클래스_이름  => T는 기반_클래스의 파생 클래스여야 한다.
where T : 인터페이스_이름  => T는 명시한 인터페이스를 반드시 구현해야 한다.
where T : U  => T는 또 다른 형식 매개 변수 U로부터 상속받은 클래스여야 한다.

11.5 일반화 컬렉션


컬렉션은 object 형식을 기반으로 하고 있기에
모든 형식 데이터를 담을 수 있는 장점이 있다.
하지만 object 형식을 기반으로 하기 있기에 성능의 문제를 가지고 있기도 하다.
컬렉션의 요소에 접근할 때 마다 형식 변환이 일어나야 하기 때문이다.

일반화 컬렉션은 컴파일 할 때 사용할 형식이 결정되기에
쓸데 없는 형식 변환을 일으키지 않아 성능에 문제가 없다.

System.Collections.Generic 네임스페이스에 다양한 컬렉션 클래스를 담고 있다.

List<T>, Queue<T>, Stack<T>, Dictionary<TKey,TValue>

위의 4개 일반화 클래스는 비일반화 클래스와 사용법과 기능이 같다.
단, 형식 매개 변수로 입력한 형식 외에는 입력을 허용하지 않는다.

-예외 처리하기-

12.1 예외에 대하여

예외 처리가 왜 중요하냐 하면, 프로그램이 실행중에 아무런 메세지 없이
그냥 오류가 나서 종료되어 버린다면 아무도 그 프로그램은 사용하지 않을 것이다.
그나마 고객에게 덜 욕먹기(?) 위해서는
무슨 오류가 나서 종료되는지 알려주는 것이 예외처리다.

12.2 try~catch로 예외 받기

예외를 받을 때 아래와 같은 형식으로 하면 된다.
try
{
    //실행하고자 하는 코드
}
catch(예외_객체1)
{
   //예외가 발생했을 때의 처리
}
catch(예외_객체2)
{
   //예외가 발생했을 때의 처리
}

12.3 System.Exception 클래스

try
{
}
catch(IndexOutOfRangeException e)
{
   //...
}
catch(DivideByZeroException e)
{
   //...
}

위와 같은 형식을 하나의 catch 절로 처리 할 수 있다.
try
{
}
catch( Exception e )
{
   //.....
}

Exception 클래스를 무조껀  사용하는 것은 금물이다.
프로그래머가 발생할 것으로 계산한 예외 말고도 다른 예외까지 받아내기에
처리하지 않아야 할 예외까지 처리하는 일은 없도록 해야 한다.

12.4 예외 던지기


try~catch 문으로 예외를 받는다는 것은 어디선가 예외를 던진다는 것이다.
예외를 던지는 법은 throw문을 이용해서 던진다.

try
{
   //...
   throw new Exception("예외를 던집니다.");
}
catch(Exception e)
{
   Console.WriteLine(e.Message);
}

12.5 try~catch 와 finally


try 블록에서 코드를 실행하다가 예외를 던져지면 바로 catch 절로 바로 뛰어 넘어온다.
예외 때문에 try블록 안에 있는 중요한 코드를 미처 실행하지 못하고 예외 처리가 된다.
예외 처리가 되어도 반드시 실행해야 하는 코드는 finally 블록안에 지정하면 된다.

try
{
   //...
   dbconn.Open(); //DB 연결
}
catch(Exception e)
{
   Console.WriteLine(e.Message);
}
finally
{
   dbconn.Close(); //예외처리가 되어도 반드시 db 연결은 끊는다.
}

12.6 사용자 정의 예외 클래스 만들기


Exception 클래스를 상속하면 새로운 예외 클래스를 만들 수 있다.

class MyException : Exception
{
   //...
}


2015년 3월 6일 금요일

뇌자극 C# 5.0 - 10강

-배열과 컬렉션, 그리고 인덱서-


10.1 All for one, one for all


같은 성격을 띤 다수의 데이터를 한번에 다뤄야 하는 경우 배열을 사용한다

데이터형식[] 배열이름 = new 데이터형식[용량]

예제)

int[] scores = new int[5];

scores[0] = 80;
scores[1] = 74;
scores[2] = 81;
scores[3] = 90;
scores[4] = 34;

foreach(int score in scores)
   Console.WriteLine(score);

10.2 배열을 초기화 하는 세 가지 방법


1) string[] array1 = new string[3]{ "배열의", "용량을", "명시하는 방법"};

2) string[] array2 = new string[]{ "배열의", "용량을", "생략하는 방법"};

3) string[] array3 = { "new키워드도", "생략하고", "초기화하는 방법"};

10.3 알아두면 삶이 윤택해지는 System.Array


Array 클래스의 주요 메소드와 프로퍼티

Sort() : 배열을 정열
BinarySearch<T>() : 이진 탐색을 수행합니다.
IndexOf() : 배열에서 찾고자하는 특정 데이터의 인덱스를 반환
TrueForAll<T>() : 배열의 모든 요소가 지정한 조건에 부합하는지 여부를 반환
FindIndex<T>() : 배열에서 지정한 조건에 부합하는 첫 번째 요소의 인덱스 반환
Resize<T>() : 배열의 크기를 재조정
Clear() : 배열의 모든 요소를 초기화
ForEach<T>() : 배열의 모든 요소에 대해 동일한 작업을 수행
GetLength() : 배열에서 지정한 차원의 길이를 반환
Length : 배열의 길이를 반환
Rank : 배열의 차원을 반환

10.4 2차원 배열


데이터형식[,] 배열이름 = new 데이터형식[2차원길이, 1차원길이];

예제)
int[, ]array = new int[2,3];
array[0,0] = 1;
array[0,1] = 2;
array[0,2] = 3;
array[1,0] = 4;
array[1,2] = 5;
array[1,3] = 6;

2차원 코드를 읽을 때는 []안에 있는 차원의 길이를 뒤에서 읽으면 이해하기 쉽다.
예를 들어 int[2,3]은

1,2,3
4,5,6

이다.

2차원 배열을 선언과 동시에 초기화 하는법

int[,] arr = new int[2,3]{ {1,2,3}, {4,5,6} };  //배열의 형식과 길이 명시
int[,] arr = new int[,]{ {1,2,3}, {4,5,6} };  //배열의 길이 생략
int[,] arr = { {1,2,3}, {4,5,6} };  //형식과 길이를 모두 생략

10.5 다차원 배열


차원이 둘 이상인 배열

예제)
int[, ,] array = new int[4,3,2]
                {
                    {{1,2},{3,4},{5,6}},
                    {{1,4},{3,4},{5,3}},
                    {{4,6},{5,5},{2,1}},
                    {{3,5},{3,6},{5,5}}
                }

10.6 가변 배열


직사각형이 아닌 필요한 부분만 데이터를 넣는 배열 형식

데이터형식[][] 배열이름 = new 데이터형식[가변 배열의 용량][];

예제)
int[][] jagged = new int[3][]
jagged[0] = new int[5]{1,2,3,4,5};
jagged[1] = new int[]{10,20,30};
jagged[2] = new int[]{100, 200};

[1][2][3][4][5]
[10][20][30]
[100][200]

10.7 컬렉션 맛보기


컬렉션 = 같은 성격을 띄는 데이터의 모음을 담은 자료구조
ArrayList, Queue, Stack, HashTable

10.7.1 ArrayList


배열과 비슷한 컬렉션이지만 용량을 지정할 필요 없이
자동으로 용량이 바뀌는 특성이 있다.
그리고 배열과 다르게 다양한 형식의 객체를 담을 수 있다.

예제)
ArrayList list = new ArrayList();
list.Add(10);  //인덱스 번호는 0부터 시작
list.Add(20);  //1
list.Add(30);  //2

list.RemoveAt(1);  //20을 삭제
list.Insert(25, 1);  //25를 1번 인덱스에 삽입 즉, 10과 20사이에 25를 삽입

10.7.2 Queue


Queue는 대기열이라는 뜻이다.
데이터나 작업을 차례대로 입력해뒀다가 입력된 순서대로 하나씩 꺼내 처리하기 위해 사용
데이터 입력시 = Enqueue() 메소드 사용
데이터 출력시 = Dequeue() 메소드 사용

예제)
//큐에 데이터 입력
Queue que = new Queue();
que.Enqueue(1);
que.Enqueue(2);
que.Enqueue(3);
que.Enqueue(4);

//큐에서 데이터 출력
while(que.Count > 0)
     Console.WriteLine(que.Dequeue());


10.7.3 Stack


스텍은 큐와 반대로 먼저 들어온 데이터가 나중에 나가고,
나중에 들어온 데이터가 먼저 나가는 구조의 컬렉션

데이터 입력시 = Push() 메소드 사용
데이터 출력시 = Pop() 메소드 사용

예제)

Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
stack.Push(3);

int a = (int)stack.Pop();  // a = 3

10.7.4 Hashtable


Hashtable는 키(Key)와 값(Value)의 쌍으로 이루어진 데이터를 다룰 때 사용
탐색 속도가 빠르고, 사용하기도 편하다.

Hashtable ht = new Hashtable();
ht["book"] = "책";
ht["cook"] = "요리사";
ht["tweet"] = "지저귀다";

Console.WriteLine( ht["book"]);
Console.WriteLine( ht["cook"]);
Console.WriteLine( ht["tweet"]);

해쉬테이블 컬렉션은 키 데이터를 그대로 사용한다. 어떤 형식이든 키로 사용가능하다.
키를 이용해서 단번에 데이터가 저장되어 있는 컬렉션 내의 주소를 계산한다.
이 작업을 해싱이라고 한다.

10.8 인덱서


인덱서는 인덱스를 이용해서 객체 내의 데이터에 접근하게 해주는 프로퍼티
객체를 배열처럼 사용할 수 있게 해준다.

class 클래스이름
{
    한정자 인덱서형식 this[형식 index]
    {
       get
       {
          //index를 이용하여 내부 데이터 반환
       }
       set
       {
          //index를 이용하여 내부 데이터 저장
       }
    }
}

예제)
class MyList
{
    private int[] array;

    public MyList()
    {
       array = new int[3];
    }
    public int this[int index]
    {
       get
       {
           return array[index];
       }
       set
       {
          if(index >= array.Length)
          {
              Array.Resize<int>(ref array, index + 1);
              Cosole.WriteLine("Array Resized : {0}", array.Length);
          }
          array[index] = value;
       }
    }
}

프로퍼티는 객체 내의 데이터에 접근할 수 있도록 하는 통로다.
인덱서도 프로퍼티처럼 객체 내의 데이터에 접근할 수 있도록 하는 통로다.
프로퍼티와 다른 점은 인덱스를 이용한다는 것.

뇌자극 C# 5.0 - 8~9강

-8강 인터페이스와 추상 클래스-


8.1 인터페이스의 선언


interface 인터페이스이름
{
   반환형식 메소드이름1( 매개_변수_목록 );
   반환형식 메소드이름2( 매개_변수_목록 );
   반환형식 메소드이름3( 매개_변수_목록 );
   반환형식 메소드이름4( 매개_변수_목록 );
   ......
}

인터페이스는 메소드, 이벤트, 인덱서, 프로퍼티만 가질 수 있다.
그리고 구현부와 접근 제한자도 없다. 무조껀 public로 선언된다.
인스턴스도 만들 수 없다.
단, 인터페이스를 상속 받는 클래스의 인스턴스를 만드는 것은 가능

8.2 인터페이스는 약속이다


인터페이스는 PC에 있는 USB 포트와 비슷하다.
모양이 규격화 되어 있어서 모양만 맞으면 어떤 제품이던 꽂아서 쓸수 있다.
인터페이스에 선언된 메소드들은 상속 받는 클래스에서 반드시 구현을 해야만 한다.
꼭 들어가야 하는 메소드를 인터페이스로 선언해 놓는 것이 좋다.

8.3 인터페이스를 상속하는 인터페이스


기존 인터페이스에 새로운 기능을 추가한 인터페이스를 만들고 싶을 때
인터페이스를 상속하는 인터페이스를 만들면 된다.

interface 파생인터페이스 : 부모인터페이스
{
    //..... 추가할 메소드 목록
}

8.4 여러개의 인터페이스, 한꺼번에 상속하기


클래스는 여러 클래스를 한꺼번에 상속할 수 없다.
C#에서는 클래스의 다중 상속을 허용하지 않는다.
하지만 인터페이스는 내용이 아닌 외형을 물려주기 때문에
인터페이스의 다중 상속은 허용한다.

예제)
interface IRunnable
{
   void Run();
}
interface IFlyable
{
   void Fly();
}

class FlyingCar : IRunnable, IFlyable  //인터페이스 다중 상속
{
    public void Run()
    {
        Console.WriteLine("Run! Run!");
    }
    public void Fly()
    {
        Console.WriteLine("Fly! Fly!");
    }
}

8.5 추상 클래스 : 인터페이스와 클래스 사이


추상 클래스는 인터페이스와 달리 "구현"을 가질 수 있다.
하지만 클래스와는 달리 "인스턴스"는 가질 수 없다.
그리고 "추상 메소드"를 가질 수 있다

추상 클래스 선언하는 법

abstract class 클래스이름
{
   //클래스와 동일하게 구현
}

예제)
abstract class AbstractBase
{
   public abstract void SomeMethod();
}

class Derived : AbstractBase
{
   public override void SomeMethod();
   {
      //Something
   }
}

추상 메소드는 추상 클래스를 사용하는 프로그래머가 그 기능을 정의하도록 강제하는 장치

-9강 프로퍼티-


9.1 public 필드의 유혹


프로퍼티를 이용하면 으닉성과 편의성을 다 누릴수 있다.
자바 같은 경우에는 아래와 같이 쓴다.

class MyClass
{
   private int myField;
   public int GetMyField() { return myField;}
   public void SetMyField() ( int NewValue ) { myField = newValue;}
}

MyClass obj = new MyClass();
obj.SetMyField(3);

하지만 C#은 프로퍼티를 지원한다.

9.2 메소드보다 프로퍼티


프로퍼티를 선언하는 문법

class 클래스이름
{
   데이터형싱 필드이름;

   접근한정자 데이터형식 프로퍼티이름
   {
       get
       {
           return 필드이름;  
       }

       set
       {
            필드이름 = value;
       }
   }
}

get/set은 접근자라고 한다.
get 접근자는 값을 읽어 오고, set 접근자는 필드에 값을 할당 한다.
위의 소스를 프로퍼티로 바꿔보겠다.

class MyClass
{
   private int myField;
   public int MyField
   {
      get
      {
         return myField;
      }
      set
      {
          myField = newValue;
      }
   }
}

MyClass obj = new MyClass();
obj.MyField = 3;

set 접근자를 구현하지 않으면 해당 프로퍼티는 읽기 전용이 된다.

9.3 자동 구현 프로퍼티


C# 3.0 이상부터는 코드를 더 단순하게 만드는 자동 구현 프로퍼티를 도입했다.

예제)
public class NameCard
{
    private string name;
    private string phoneNumber;

    public string Name
    {
       get { return name; }
       set { name = value; }
    }
    public string PhoneNumber
    {
       get { return phoneNumber; }
       set { phoneNumber = value; }
    }
}

를 자동 구현 프로퍼티로 바꾸면....

public class NameCard
{
    public string Name
    {
       get; set;
    }

    public string PhoneNumber
    {
        get; set;
    }
}

9.4 프로퍼티와 생성자


객체를 생성할 때 각 필드를 초기화 하는 또 다른 방법은 프로퍼티를 이용한 초기화다.

클래스이름 인스턴스 = new 클래스이름()
{
    프로퍼티1 = 값,
    프로퍼티2 = 값,  //세미클론(;)이 아니라 콤마(,) 다
    프로퍼티3 = 값
}; //세미콜론(;)이 붙어야 한다.

모든 값이 올 필요는 없고 초기화 하고 싶은 프로퍼티만 넣어서 초기화 하면 된다.

예제)
BirthdayInfo birth = new BirthdayInfo()
{
   Name = "서현",
   Birthday = new DateTime(1991, 6, 28)
};

내가 서현을 좋아하긴 하지만....;; 진짜 책에 이리 예제가 되어 있다...;

9.5 무명 형식


말 그래도 이름이 없는 형식이다. 무명 형식은 선언과 동시에 인스턴스를 할당한다.
인스턴스를 만들고 다시는 사용하지 않을 때 요긴하게 쓰인다.

예)
var myInstance = new { Name = "윤아", Age="25" }; //new 다음에 이름이 없다.

9.6 인터페이스의 프로퍼티


인터페이스는 메소드 뿐만 아니라 프로퍼티와 인덱서도 가질 수 있는데
프로퍼티나 인덱서를 가진 인터페이스를 상속하는 클래스가 "반드시" 해당 프로퍼티와
인덱서를 구현해야 한다.
인터페이스의 프로퍼티 선언이 클래스의 자동 구현 프로퍼티 선언과 동일하다.

예제)
interface IProduct
{
    string ProductName
    {
        get; set;
    }
}

class Product : IProduct
{
    private string productName;
    public string ProductName
    {
       get { return productName; }
       set { productName = value; }
    }
}

9.7 추상 클래스와 프로퍼티


추상 프로퍼티 문법

abstract class 추상클래스이름
{
   abstract 데이터형식 프로퍼티이름
   {
      get; set;
   }
}

예제)

abstract class Product
{
   private static int serial = 0;
   public string SerialID
   {
      get { return String.Format("{0:d5}", serial++) }
   }
   abstract public DateTime ProductDate
   {
       get; set;
   }
}

class MyProduct : Product
{
   public override DateTime ProductDate
   {
      get; set;
   }
}