강께르의 개발일지

[수업] 20210923_6일차 본문

프로그래밍/C#

[수업] 20210923_6일차

강께르 2021. 9. 24. 00:27

델리게이트의 연장선

 

이벤트 키워드

 

이벤트 키워드는 델리게이트의 일종으로 Publisher와 Subscriber의 관계를 이용해 Publisher의 이벤트 호출로 인해 등록된 Subscriber에게 일괄적으로 함수를 호출하게 하는 메시지 방식의 느낌을 띄는 것 같다.

 

public event (이벤트 델리게이트 데이터형 이름) (이름);

으로 선언한다.

 

하지만 보통 system에서 제공하는 이벤트 델리게이트를 사용한다.

public event eventhandler (이름);

eventhandler는 반환형이 void이며 매개변수가 (object sender, EventArg e)를 받는다.

 

아직까지는 매개변수로 전달받은 것으로 어떻게 굴리는지 잘 모르겠다. 하지만 이 델리게이트와 연결된 메소드를 지닌 클래스에게 특정 이벤트가 발생 시, 메시지 방식처럼 동작해 특정 함수를 호출해 무언가를 하게끔 한다는 것은 이해하겠다.

 

event 한정자가 붙은 델리게이트는 프로퍼티의 get, set처럼 add와 remove를 정의할 수 있다.

이 add와 remove는 연산자의 +=와 -=와 연결되는 부분이다. = 연산자로 할당은 되지 않는다고 한다.

외부에서 add와 remove를 자유롭게 할 수 있으나, 호출은 내부에서만 가능하다.

 

마지막 리스너에 대한 에러가 발생하는 것을 피하기 위해 내부적으로 호출할 때 지역변수에 한번 복사하고 호출해야한다.

 

다음에 좀더 사용하는 것에 대해 공부하는 게 좋겠다.

 

람다

델리게이트와 같이 쓴다. 델리게이트 매개변수란에 함수 이름 대신 람다문을 작성해 넣는다.

람다문으로 프로퍼티 내부를 대체 가능하다.

메소드를 람다문으로 작성이 가능하다.

 

두 개 이상일 때는...

소괄호도 문법이다. => : 람다식 연산자

(parameters) => expression or statement block

 

반환형은? 식의 연산 후 반환형이랑 델리게이트의 반환형이랑 똑같아야함

(int x) => x * x;

x => { return x * x; }

 

detail

 

Func 델리게이트에 람다식을 추가할 수 있다.

대신 매개변수와 반환형의 일치는 신경써야한다.

 

제네릭과 람다식을 같이 쓸 때, 일반화 인자로 어떤 데이터형이 들어갈지 명시되어야한다.

Bar(x => Foo(x))

Bar((int x) => Foo(x))

Bar<int>(x => Foo(x))

Bar<int>(Foo)

 

지역 메소드, 내부 메소드와 관련된 주제

람다식의 장점 : 메소드로 사용 시 매개변수를 따로 추가하고 지역변수를 끌어다 쓰는 느낌인데

람다식으로 하면 지역변수를 람다식으로 바로 사용이 가능하다.

 

델리게이트에 추가될 때, 람다식 내에 지역 변수가 있을 경우 서순을 중요시하자.

중간에 값이 변경되는 일이 있다면 원하는 값이 나오지 않을 가능성이 있다.

 

return 자리에 델리게이트로 람다식을 반환하여 그 안에 지역변수를 반환한다면,

그 메소드에 대한 참조가 살아있는 이상 지역 변수는 계속 살아있는다.

델리게이트의 참조가 사라지면 그 지역변수도 소멸한다.

이 케이스는 좋지 않으니 지양하자.

 

람다식 static 키워드

부작용이 있을 것 같은 부분에 static을 붙여 체크할 수 잇게 가능

안에서 쓰는 경우에는 부작용이 없고 스코프 외부로 나갈 때에 부작용이 있기에 그 상황을 지양하라는 뜻

 

for문도 이 경우가 해당할 수 있다. 포문에 사용하는 인덱스를 모두 같이 참조하면 같은 값이 나올 수 있다.

 

대안

새로운 변수가 스코프 안에서 계속 메모리에 할당될 수 있게 만들어 람다식과 연결하면 된다.

 

람다식의 단점 : 재귀함수 호출이 안된다.

 

어지간한 상황에 람다식을 쓰면 깔끔한 코드를 작성할 수 있다.

 

무명 메소드

델리게이트 할당 시, 이 키워드는 무명메소드를 쓰겠다는 의미이다.

반환형도 유추 가능하다. 왼쪽에 자리할 델리게이트 변수에 의해 가능하다.

delegate (int x) { return x * x; }

 

무명 메소드는 깡통으로 만들어 델리게이트 변수에 null인 것을 방지할 수 있다.

 

멤버 선언으로 람다식을 쓸 수 있다.

 

델리게이트에 할당할 수 있는 것 : 지역메소드, 람다식, 무명메소드

 

예외처리

예외가 발생하면 프로그램이 죽는다. -> 비정상적인 종료

죽지 않고 계속 프로그램을 작동시켜 돌아가게 해야하는 방법

예외가 발생할 수 있는 곳에 싹 다 예외처리를 해준다.

예 : 파일 입출력, 네트워크 사용 등 프로그래머가 컨트롤할 수 없지만 치명적인 것

 

그 외 프로그래머가 컨트롤할 수 있는 것은 예외처리를 쓰지 않는 방법으로 도와주는 메소드를 제공하기에 그걸 쓰자.

 

try

exception이 발생할 수 있는 부분을 이 안에 넣자.

만약 exception이 발생했다면 그 즉시 진행을 멈추고 메소드나 내부 클래스 동작에서 try 구문까지 나올 것이다.

그 다음 발생한 exception에 대해서 catch 구문에 넘긴다.

 

catch

try에 넘겨받은 exception에 따라 특정 처리를 진행한다.

만약 nullReferenceException이 발생해 catch가 이를 처리할 수 있다고 한다면 스코프 내에 있는 코드를 수행하는 것이다. 오버로딩이 가능하다.

 

finally

try-catch가 예외처리를 하든지 말든지 상관없이 무조건 이 부분을 호출한다.

예를 들어 파일 입출력을 위해 리소스를 열었는데 try부분에 강제적으로 catch 부분으로 넘어와 리소스를 닫는 줄을 건너뛰는 상황을 방지하기 위해 finally에서는 그 부분을 수행하는 일을 예로 쓴다.

 

* IDisposable -> Dispose로 열어놓은 파일 닫기에 쓰이는 인터페이스 및 메소드

 

예외처리 관련 키워드 using

제약사항 : IDisposable 인터페이스를 상속 받은 클래스만 이 키워드를 사용가능하다.

예외 발생하든 말든 using 구문 안에 있는 열어놓은 파일은 무조건 Dispose를 호출한다.

사용 권장하는 키워드이다.

using (StreamReader reader = File.OpenText("file.txt")) { ... }

{
    StreamReader reader = File.OpenText("file.txt");
    try
    {
        ...
    }
    finally
    {
        if (reader != null)
            ((IDisposable)reader).Dispose();
    }
}

 

try-catch는 코드 가독성면에선 쓰지 않는 게 좋아 보이나

서버 부분과 같이 매우 중요하며, 프로그래머가 컨트롤하기 힘든 부분에서는 필수적으로 쓴다.

중간에 죽어도 되는 것이라면... 쓰지 않고 메소드로 예외를 회피할 수 있게 만드는 것이 좋을 수 있다.

TryParse

 

매개변수 없는 catch는 모든 예외를 걸러준다.

 

필터링 가능

when 키워드로 조건에 맞는 필터링 사용 가능

 

throw exception

nameof 연산자 : 변수의 이름을 return

exceptionmessge에 담아서 출력 가능

throwsystem.exception을 상속받은 객체를 new 해줘서 사용해야한다.

throw를 만나면 중단하고 콜스택을 타고 돌아간다.

throw를 했지만 catch에서 못 걸러주면 프로그램이 뻗는다.

 

람다식 형태로도 가능하다. 3항 연산자에서의 피연산자 중 하나로 사용 가능하다.

 

catch throw?

메인 메소드에서의 throw가 아닌 스택처럼 쌓인 구조 속에서 윗단에 throw를 넘겨줘야할 상황일 경우 사용, 그냥 throw만 사용하면 같은 exception을 그대로 전달한다.

윗단에서 받아줄 try catch문이 없으면 프로그램이 죽는다.

 

예외가 발생되는 상황을 한번씩 msdn을 보자.

 

열거자 인터페이스

IEnumerable<T> IEnumberator<T>

열거자

만들어놓으면 foreach를 사용할 수 있게 된다.

구체적으로 이야기하면 컬렉션은 아니다. IEnumerable를 상속한 클래스가 되는 것.

 

IEnumerable<T>을 상속받은 클래스는

GetEnumerator 메소드를 재정의해야한다.

이는 상속받은 클래스 내에 정의된 Enumerator를 새로 생성해 반환하는 역할을 가진다.

 

상속받은 클래스는 IEnumerator를 상속받은 클래스를 정의해서 사용할 수 있다.

이 클래스는 현재 요소를 갖는 Current 프로퍼티, 순회에 관련된 메소드인 MoveNext() 메소드

처음부터 다시 순회하게 하는 Reset 메소드를 상속받아 재정의 해야한다.