2015년 7월 21일 화요일

정말 간단하게 GO언어를 요약해봤다.

책을 도서관에서 빌려 온 거라 시간이 없기도 했지만
많은 부분은 아는 부분이라 건너 뛴 것도 많다 -_-;

Go언어는 한국어로 튜토리얼이 되어 있다.(/만세/)

튜토리얼로 하나하나 공부하다 보면 Go언을 쉽게 익힐 수 있을 것이다.

그리고 책도 구입해서 한다면 더 좋을 듯 싶다.

추천하는  책은 "가장 빨리 만나는 GO언어" 다.

안드로이드가 오라클과의 소송에서 패배함으로
안드로이드에 쓰인 자바 언어를 go언어로 바꿀지도 모른다는 생각으로 빠르게 함 봤다.

튜토리얼 주소 : http://go-tour-kr.appspot.com/#1

당분간은 JSP 공부 하느라 글을 못 올릴 것 같다.
jsp는 이곳에 게시하기가 애매하다 -_-;;;

GO 언어 기본 문법 - 9

--------------정렬 활용하기------------
func Sort(data Interface)  //데이터를 오름차순으로 정렬
func Reverse(data Interface) Interface //데이터를 내림차순으로 정렬
type IntSlice []int //int 정렬 인터페이스
type Float64Slice []float64 //float64 정렬 인터페이스
type StringSlice []string //string 정렬 인터페이스

----------컨테이너 사용하기------------
Go언어는 기본 자료구조를 패키지로 제공한다.
연결리스트: 각 노드를 한 줄로 연결한 자료구조
힙: 이진 트리를 활용한 자료구조
링: 각 노드가 원형으로 연결되 자료구조

연결 리스트 사용하기 - container/list 패키지
func New() *List   //연결 리스트 생성
func (L *List)PushBack(v interface{}) *Element  //연결 리스트의 맨 뒤에 데이터 추가
func(L *List)Front() *Element  //연결리스트의 맨 앞 데이터를 가져옴
func(L *List)Back() *Element   //연결리스트의 맨 뒤 데이터를 가져옴

힙 사용하기 - container/heap 패키지
func Init(h Interface)  //힙 초기화
func Push(h Interface, x interface{})  //힙에 데이터 추가

링 사용하기 - container/ring 패키지
func New(n int) *Ring  //링 생성
func (r *Ring) Do(f func(interface{}))  //링의 모든 노드 순회
func (r *Ring) Move(n int) *Ring  //링을 회전시킴. 매개 변수로 양수를 넣으면 시계방향, 음을 넣으면 반 시계 반향으로 회전

----------TCP 프로토콜 사용하기------------

서버 작성하기 -net 패키지

func Listen(net, laddr string)(Listener, error) //프로토콜, IP주소, 포트 번호를 설정하여 연결 대기
func(I *TCPListener)Accept() (Conn, error) //클라이언트가 연결되면 TCP 연결
func(I *TCPListener)Close() error //TCP 연결 대기를 닫음
func(c *TCPConn)Read() (b []byte)(int, error) //받은 데이터 읽기
func(c *TCPConn)Write() (b []byte)(int, error) //데이터 보내기
func(c *TCPConn)Close() error  //TCP 연결을 닫음

클라리언트 작성하기 - net 패키지

func Dial(network, address string)(Conn, error) //프로토콜, IP주소, 포트 번호를 설정, 서버에 연결
func(c *TCPConn) Close() error //TCP 연결을 닫음
func(c *TCPConn) Read(b []byte)(int, error) //받은 데이터를 읽음
func(c *TCPConn) Write(b []byte)(int, error) //데이터를 보냄


GO 언어 기본 문법 - 8

-----------파일 쓰기---------------------
func Create(name string)(file *File, err error)  //기존 파일을 열거나 새 파일을 생성
func(f *File)Close() error  //열린 파일을 닫음
func(f *File)Write(b []byte)(n int, err error)  //파일에 값을 씀. 파일에 쓴 데이터의 길이와 에러 값을 리턴

----------파일 읽기---------------------
func Open(name string)(file *File, err error) //파일 열기
func (f *File) Stat()(fi FileInfo, err error)  //파일의 정보를 얻어옴
func (f *File) Read(b []byte) (n int, err error) //파일에서 값을 읽음

----------파일 읽기 쓰기---------------
//파일 플래그, 파일 모드를 지정하여 파일 열기
func OpenFile(name string, flag int, perm FileMode)(file *File, err error)

//파일을 읽거나 쓸 위치로 이동
func (f *File)Seek(offset int64, whence int)(ret int64, err error)

------------압축 사용하기------------------
//io.Reader인터페이스로 io.Reader 인터페이스를 따르는 압축 해제 인스턴스 생성
func NewReader(r io.Reader)(*Reader, error)

//ioWriter 인터페이스로 io.Writer 인터페이스를 따르는 압축 인스턴스 생성
func NewWriter(w io.Writer)*Writer

//io.Reader를 끝까지 읽어서 바이트 슬라이스로 리턴
func ReadAll(r io.Reader)([]byte, error)

---------------암호화 사용하기---------------

(해쉬 알고리즘 사용하기)-crypto/sha512 패키지

func New() hash.Hash   //SHA512 해시 인스턴스 생성
func Sum512(data []byte) [Size]byte  //SHA512 해시를 계산하여 리턴
func (d *digest)Write(p []byte)(nn int, err error)  //해시 인스턴스에 데이터 추가
func(d0 *digest)Sum(in []byte)[]byte //해시 인스턴스에 저장된 데이터의 SHA512해시값 추출

(AES 대칭킹 알고리즘 사용하기)-crypto/aes 패키지

func NewCipher(key []byte)(cipher.Block, error) 대칭키 암호화 블록 생성
func (c *aesCipher)Encrypt(dst, src []byte) 평문을 AES알고리즘으로 암호화
func(c *aesCipher)Decrypt(dst, src []byte) AES 알고리즘으로 암호화된 데이터를 평문으로 복호화

(RSA 공개키 알고리즘 사용하기)-crypto/rsa 패키지

//개인키와 공개키 생성
func GenerateKey(random io.Reader, bits int)(priv *PrivateKey, err error)

//평문을 공개키로 암호화
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte)(out [[]byte, err error)

//암호화된 데이터를 개인키로 복호화
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte)(out []byte, err error)
















GO 언어 기본 문법 - 7

---------인터넷 소스 저장소의 패키지 사용하기-----------
Go 언어는 로컬 있는 패키지도 지원하지만
인터넷 소스 저장소에 올라와 있는 패키지도 사용할 수 있다

import에 패키지 주소만 설정하면 인터넷 저장소의 패키지를 사용가능
import (
   "fmt"
   "github.com/golang/exdample/stringutil"
)

go get 명령을 입력해서 패키지를 받음

----------패키지의 함수, 변수, 상수를 외부에 노출----------
패키지를 외부에서 사용할 수 있게 하려면 첫글자를 대문자로 지정해야 한다

-------------문서화---------------------
패키지, 함수, 변수, 상수 위에 //주석을 작성하면 설명을 문서화 할수 있다

-------------출력 함수----------------
func Print()  //값을 그 자리에서 출력(새 줄로 넘어가지 않음)
func Println() //값을 출력한 뒤 새 줄로 넘어감(개행)
func Printf()  //형식을 지정하여 값을 출력

--------------입력 함수-----------------
func Scan() //콘솔에서 공백, 새 줄로 구분하여 입력을 받음
func Scanln() //콘솔에서 공백으로 구분하여 입력을 받음
func Scanf() //콘솔에서 형식을 지정하여 입력을 받음

---------------문자열 입출력 함수--------------------
func Sprint() string  //값을 그대로 문자열로 만듦
func Sprintln() string //값을 그대로 문자열로 만든 뒤 문자열 끝에 개행 문자 붙임
func Sprintf() string  //형식을 지정하여 문자열을 만듦

func Sscan() string  //공백, 개행문자로 구분된 문자열에서 입력을 받음
func Sscanln() string //공백으로 구분된 문자열에서 입력을 받음
func Sscanf() string  //문자열에서 형식을 지정하여 입력을 받음

----------------파일 입출력 함수-------------------
func Create(name string)(file *File, err error) //기존 파일을 열거나 새 파일을 생성
func Open(name string)(file *File, err error) //기존 파일 열기
func(f *File) Close() error //열린 파일 닫음

--------------------문자열 처리하기------------------------
func Contains(s, substr string)bool   //문자열이 포함되어 있는지 검색
func ContainsAny(s, chars string)bool //특정 문자가 하나라도 포함되어 있는지 검색
func ContainsRune(s string, r rune)bool  //rune 자료형으로 검색
func Count(s, sep string)int   //문자열이 몇 번 나오는지 구함
func HasPrefix(s, prefix string)bool  //문자열이 접두사인지 판단
func HasSuffix(s, suffix string)bool //문자열이 접미사인지 판단
func Index(s, sep string)int  //특정 문자열의 위치를 구함
func IndexAny(s, chars string)int  //가장 먼저 나오는 문자의 위치를 구함
func IndexByte(s string, c byte)int //byte 자료형으로 위치를 구함

------------문자열 변환 함수-------------------------
func Atoi(s string)(i int, err error)   //숫자로 이루어진 문자열을 숫자로 변환
func Itoa(i int)string  //숫자를 문자열을 변환
func FormatBool(b bool)string  //불 값을 문자열로 반환
func FormatFloat(f float64, fmt byte, prec, bitSize int)string //실수를 문자열로 변환
func FormatInt(i int64, base int)string  //부호 있는 정수를 문자열로 변환
func FormatUint(i uint64, base int)string  //부호 없는 정수를 문자열로 변환












2015년 7월 20일 월요일

GO 언어 기본 문법 - 6

----------고루틴 사용하기-----------
고루틴은 함수를 동시에 실행시키는 기능이다
다른 언어의 스레드 생성보다 문법이 간단하고, 스레드 보다 리소스를 적게 사용한다

함수 호출 시에 go 키워드를 붙이면 해당 함수는 고루틴으로 실행된다.

예제)
package main

import "fmt"

func hello() {
   fmt.Println("Hello, world!")
}

func main() {
   go hello           //함수를 고루틴으로 실행
   fmt.Scanln()       //main 함수가 종료되지 않도록 대기
}

------------멀티코어 활용하기-------------
GO 언어는 CPU 코어를  한 개만 사용하도록 설정되어 있다.
다음은 시스템의 모든 CPU 코어를 사용하는 방법이다.

package main

import (
   "fmt"
   "runtime"
)

func main() {
     //CPU 개수를 구한 뒤 사용할 최대 CPU 개수 설정
   runtime.GOMAXPROCS(runtime.NumCPU()) 
   fmt.Println(runtime.GOMAXPROCS(0))  //설정 값 출력
   s := "Hello, world!"

   for i:=0; i<100; i++ {
       go func(n int) {  //익명 함수를 고루틴으로 실행
          fmt.Println(s, n)
       }(i)
   }

   fmt.Scanln()  //main 함수가 종료되지 않도록 대기
}

-----------채널 사용하기-------------
채널은 고루틴끼리 데이터를 주고 받고, 실행 흐름을 제어하는 기능
make(chan 자료형)

예제)
package main

import "fmt"

func sum(a int, b int, c int) {
   c <- a+b   //채널에 a와 b의 합을 보냄
}

func main() {
   c := make(chan int)  //int 형 채널 생성
   go sum(1, 2, c)        //sum을 고루틴으로 실행한 뒤 채널을 매개변수로 넘겨줌
   n := <-c                //채널에서 값을 꺼낸 뒤 n에 대입
   fmt.Println(n)          //3
}

채널을 사용하기 전에는 반드시 make 함수로 공간을 할당
채널을 매개변수로 받는 함수는 반드시 go키워드를 사용하여 고루틴을 실행해야 한다

함수에서 채널을 매개변수로 받을 때는
변수명 chan 자료형 
형식으로 한다

채널에 값을 보낼 때
채널 <- 값

채널에서 값을 가져올 때
<- 채널

채널 버퍼링
make(chan 자료형, 버퍼개수)

채널에 버퍼를 1개이상 설정하면 비동기 채널이 생성된다.
비동기 채널은 보내는 쪽에서 버퍼가 가득 차면 실행을 멈추고 대기하며
받는 쪽에서는 버퍼에 값이 없으면 대기한다.

Go언어는 여러 채널을 손쉽게 사용할 수 있도록 select 분기문을 제공
select {case <-채널:코드}

select {
case <-채널1:
  //채널1에 값이 들어왔을 때 실행할 코드를 작성
case <-채널2:
  //채널2에 값이 들어왔을 때 실행할 코드를 작성
default:
  //모든 case의 채널에 값이 들어오지 않았을 때 실행할 코드를 작성
}


GO 언어 기본 문법 - 5

--------인터페이스----------

type 인터페이스명 interface {}

인터페이스는 메서드의 집합이다. 단, 메서드 자체를 구현하지는 않는다.
인터페이스 선언하는 방법은 변수 선언법과 같다.

type 인터페이스명 interface {
    메서드1() 리턴값_자료명
    메서드2()       //리턴값이 없을 때
}
주의할 점은 메서드를 ,로 구분하지 않는다는 것이다.

예제)
package main

import (
"fmt"
"math"
)
//인터페이스 선언
type Abser interface {
Abs() float64
}

func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}

a = f  // a MyFloat implements Abser
a = &v // a *Vertex implements Abser

// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
a = v

fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

type Vertex struct {
X, Y float64
}

func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

GO 언어 기본 문법 - 4

----------포인터 사용하기-----------
var 변수명 *자료형

var numPtr *int //포인터형 변수를 선언하면 nil로 초기화 됨
fmt.Println(numPtr) //nil

Go 언어는 NULL 값을 nil로 사용함.
Go 언어는 메모리 주소를 직접 대입하거나 포인터 연산을 허용하지 않는다.
빈 포인터형 변수는 new 함수로 메모리로 할당해야 함

포인터_변수 = new(자료형)

var numPtr *int = new(int)
fmt.Println(numPtr) //메모리 주소 출력

*포인터_변수명 - 역참조

var numPtr *int = new(int)   //new 함수로 공간 할당
*numPtr = 1                     //역참조로 포인터형 변수에 값을 대입
fmt.Println(*numPtr)           //1 포인터형 변수에서 값을 가져오기

&변수명 - 해당 변수의 메모리 주소

var num int = 1
var numPtr *int = &num  //참조로 num변수의 메모리 주소를 구하여
                                  //numPtr 포인터 변수에 대입
fmt.Println(numPtr)          //numPtr 포인터 변수에 저장된 메모리 주소
fmt.Println(&num)           //참조로 num변수의 메모리 주소를 구함

-------------구조체 사용하기-----------------
구조체 - 여러 변수를 담을 수 있는 구조체

type 구조체명 struct {}

{} 블록 안에는 구조체의 필드 목록을 정의함
type Rectangle struct {
     width int
     height int
}

-----------구조체에 메서드 연결하기-------------
Go언어에는 클래스가 없는 대신 구조체에 메서드를 연결할 수 있다

func(리시버명 *구조체_타입) 함수명() 리턴값_자료형 {}

type Rectangle struct {
     width int
     height int
}
func (rect *Rectangle) area() int {
     return rect.widthrect.height
}
func main() {
      rect := Rectangle{10, 20}
      fmt.Println(rect.area()) //결과값 : 200
}

------------포인터 방식, 일반 구조체 방식-----------
//포인터 방식
func (rect *Rectangle) scaleA(factor int) { //포인터라 원래의 값이 변경 됨
    rect.width = rect.width * factor
    rect.height = rect.height * factor
}

//일반 구조체 방식
func (rect Rectangle) scaleB(factor int) {  //값이 복사되어서 원래의 값에는 영향 x
    rect.width = rect.width * factor
    rect.height = rect.height * factor
}

func main() {
   rect1 := Rectangle{30, 30}
   rect1.scaleA(10)
   fmt.Println(rect1) //{300, 300} : rect1에 바뀐 값이 들어감
 
   rect2 := Rectangle{30, 30}
   rect2.scaleB(10)
   fmt.Println(rect2) //{30, 30} : rect2는 값이 바뀌지 않음

}








GO 언어 기본 문법 - 3

------------함수---------------
함수의 중괄호
함수의 여는 중괄호를 다음 줄에 작성하면 에러
func hello()   //에러
{
   //어쩌구
}

익명함수
함수에는 이름이 없는 함수를 익명 함수라고 한다. 익명함수는 정의한 뒤 바로 호출 가능

func() {            //함수에 이름이 없음
   fmt.Println("Hello, world!")
}()

func(s string) {  //익명 함수를 정의한 후
    fmt.Println(s)
}("Hello, world!")  //바로 호출

r:= func(a int, b int) int { //익명 함수를 정의한 뒤
   return a + b
}(1,2)  //바로 호출하여 리턴값을 변수 r에 저장

클로저
클로저는 함수 안에서 함수를 선언 및 정의할 수 있고,
바깥쪽 함수에 선언된 변수에도 접근할 수 있는 함수를 말한다.
또한 클로저를 사용하면 지역변수가 소멸되지 않고,
나중에 함수를 호출할 때마다 계속 가져다 쓸수 있다.
즉, 클로저는 함수가 선언될 때의 환경을 계속 유지하여 함수의 변수에 저장할 수 있다

func calc() func(x int) int {
   a,b := 3,5   //지역 변수는 함수가 끝나면 소멸되지만
   return func(x int) int {
       return a*x + b //클로저이므로 함수를 호출 할 때마다 변수 a,b의 값을 사용
   }
}
func main() {
   f := calc() //calc 함수를 실행하여 리턴 값으로 나온 클로저를 변수에 저장
   fmt.Println(f(1))  //8
   fmt.Println(f(2))  //11
   fmt.Println(f(3))  //14
   fmt.Println(f(4))  //17
   fmt.Println(f(5))  //20
}

recover
recover 함수를 사용하면 패닉이 발생했을 때 프로그램이 바로 종료되지 않고 예외 처리를 할 수 있음

func f() {
   defer func() {               //recover 함수는 지연 호출로 사용해야 함
      s := recover()           //패닉이 발생해도 프로그램을 종료하지 않음
      fmt.Println(s)            //panic 함수에서 설정한 에러 메세지를 받아옴
   }()
   panic("Error!!!")
}
func main() {
    f()
    fmt.Println("Hello, world!")  //패닉이 발생했지만 계속 실행됨
}

GO 언어 기본 문법 - 2

--------for 반복문----------

Go 언어는 반복문이 for만 있다.

for 초기값; 조건식; 변화식 {
     //여기에 반복할 코드를 작성한다.
}

if문과 마찬가지로 조건식에 ()를 사용하지 않는다.
역시 {}를 생략해서도 안된다.

for 키워드에 조건식만 설정하면 while 문과 같다.
for 조건식 {
 //여기에 반복할 코드를 작성
 //변화식도 함께 작성
}

for 문에 조건식을 설정하지 않으면 무한루프다

반복문에서 변수 여러개 사용하기

//i가 10 보다 작을 때까지 반복하면서 i는 1씩 증가, j는 2씩 증가
for i,j := 0, 0; i<10; i,j = i+1, j+2 {
   fmt.Println(i, j)
}

----switch 분기문-----

switch 변수 {case 값: 코드}

switch 변수 {
case 값1:
     //값 1일 때 실행할 코드를 작성
case 값2:
     //값 2일 때 실행할 코드를 작성
case 값3:
     //값 3일 때 실행할 코드를 작성
default:
    //모든 case에 해당하지 않을 때 실행할 코드 작성
}

----------배열----------

var 배열명 [길이]자료형

var a [5]int    //int형이며 길이가 5인 배열 선언
a[2] = 7        //배열의 세번째 요소에 7 대입
fmt.Println(a)  //[00700]

배열을 순회할 때는 for반복문에서 range 키워드를 사용
a:=[5]int{11,22,33,44,55}
for i, value := range a {  //i에는 인덱스, value에는 배열 요소의 값이 들어감
   fmt.Println(i, value)
}

슬라이스는 길이가 고정되어 있지 않으며 동적으로 크기가 늘어난다.
a := []int{1,2,3}
var b []int //슬라이스로 선언
b = a
b[0] = 9
fmt.Println(a) //[9,2,3]
fmt.Println(b) //[9,2,3]

------------맵----------------
맵은 키-값 형태로 자료를 저장하며 레퍼런스 타입이다
aaa := make(map[string]float32) //키는 string, 값은 float32인 맵 생성 및 공간 할당
aaa["ccc"] = 222 //맵[키]=값

range로 맵 순회
맵을 순회할 때는 for반복문에서 range 키워드를 사용

//반복문이 실행될 때마다 키와 값이 자동으로 변수에 들어감
for key, value := range aaa {
     fmt.Println(key, value)
}


GO 언어 기본 문법 - 1

1) 중괄호는 반드시 구문의 맨 뒤에서 시작
func main() {
    var num int = 1
}

2) 변수 짧은  선언
변수명 := 초기값 형식으로 var 키워드를 사용하지 않고 변수를 선언
age := 10
name := "Maria"

3)병렬 할당
var x, y int
var age int

x, y, age=10, 20, 5

4)go 언어는 변수에 문자열을 저장한 뒤 내용을 수정할 수 없다.

5)문장의 끝에 세미콜론(;)을 넣을 필요 없다.

6) 변수 할당은 자료명이 뒤에 들어간다.

var num int = 1 //int 형 num변수에 1을 할당한다.

7) go언어는 문자와 문자열을 유니코드 UTF-8로 지정한다.

8) 패키지 선언 방법

(1) 단일 선언법
import "fmt"

(2) 여러개 패키지 선언
import (
   "fmt"
   "runtime"
)

(3) 전역 패키지로 사용 .(점)을 사용하면 전역 패키지가 된다.
import . "fmt"

(4) 패키지 별칭 사용
import f "fmt" //fmt를 f로 가져옴

(5) 사용하지 않는 패키지(go언어는 사용하지 않는 패키지 선언시 컴파일 에러)
import _ "fmt" //컴파일 에러 발생 안함

9) if 조건문

if 조건식 {}

if 조건식을 사용할 때 ()를 사용하지 앟으며 조건문을 시작하는 줄에서 {가 시작된다.
또한, 한줄 if 문이라도 {} 를 생략하지 않아야 한다.

예제)
i := 6
if i >= 5 {
   fmt.Println("5이상")
}else if i>=5 && i<10 {
   fmt.Println("5이상, 10미만")
}else if i>=0 && i <5 {
   fmt.Println("0 이상, 5미만")
}


Go언어 공부를 해보려고 한다.

Go 언어는 구글에서 만든 언어다.
이 언어를 공부하려는 이유는....
구글이 최근 오라클에게 소송에서 졌기 때문이다 ;;;
안드로이드에 쓰인 자바를 무단으로 썼다고 오라클이 소송을 건듯 하다;;;
안드로이드가 go언어로 바뀌지 않을까 하는 마음에 대충 훓어볼려고 한다.
절대로 도서관에서 책을 빌렸기 때문이 아니다....;;

go언어는 설치를 해야 쓸수 있는 언어다.

go 언어 다운받기 : https://golang.org/dl/

설치후에는 IDE를 다운 받아 사용하는 것이 편하다.

LiteIDE 다운 받기 : http://sourceforge.net/projects/liteide/files/
LiteIDE 다운 받기 : https://github.com/visualfc/liteide

Go 언어는 한국어로(!) 튜토리얼을 사용할 수 있다.

Go 언어를 설치하면 GoDocServer 를 실행시키면 튜토리얼을 사용할 수 있다.

또는 튜토리얼 사이트 : http://go-tour-kr.appspot.com/#1

책은 이 책을 추천한다.

"가장 빨리 만나는 Go 언어"

http://www.yes24.com/24/goods/18077092?scode=032&OzSrank=1

다음부터는 간단하게 go언어의 특징만 요약해볼꺼다~

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 로 선언되어 있어도 
자신이 선언되어 있는 클래스 외부에서는 호출이 불가능 하다

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