PROGRAMMING WORKSHOP

.Net FrameWork,VB.Net | Thread,Delegate,Lambda Expression

Lambda Expression은 간단한 작업의 경우 Delegate사용 대신에 사용한다

앞페이지에서
아래와 같이 Invoke매개변수를 Lambda Expression을 사용하였었다

Sub doBackGroundWork()
        For iX As Integer = 0 To 100
            Threading.Thread.Sleep(100)
            Invoke(Sub() Me.ProgressBar1.Value = iX)
            Invoke(Sub() Me.Text = "Processing......" + ProgressBar1.Value.ToString() + "%")
        Next
End Sub

위와 같이 프로시져나 함수를 별도로 만들지 않고 프로시져를 inline code로 작성하는 것을
Lambda Expression이라고 하고 실은 본래는
이페이지에서 이야기 할 Delegate라는 것을 사용한다
하지만 이것을 약식으로 표현한 것이 Lambda Expression이라고 보시는 것이 좋겠다

Delegate는 하자의 개체이다..
이 개체의 역할은 단순히 어떤 함수나 프로시져를 사용할지 지정하는(Pointing)하는 역할이다
아래와 같이 Delegate 타입을 선언하고..


Delegate Sub myWork(iX As Integer)

이것을 사용할때는

Dim oQ As New myWork(AddressOf doWorkBehind)

와 같이 Delegate 개체를 생성하면서, 어떤 프로시져를 사용할지 AddressOf 연산자로 지정하는 것이다
아래의 doWorkBehind라는 프로시져를 사용하겠다고 개체에 지정하고

Sub doWorkBehind(iX As Integer)
	Me.ProgressBar1.Value = iX
	Me.Text = "Processing....." + Me.ProgressBar1.Value.ToString + "%"
End Sub

이 개체를 필요한 곳에 사용하면 된다
즉 아래와 같이 Invoke메소드에서 사용할수 있는 것이다

Sub doBackGroundWork()
	For iX As Integer = 0 To 100
		Threading.Thread.Sleep(100)
		Invoke(New myWork(AddressOf doWorkBehind), iX)
	Next
End Sub

이것은 런타임에 어떤 프로시져를 실행할지 융통성이 있게 되는 것

이벤트프로시져등에서 AddressOf 연산자로 사용할 프로시져를 지정했었다
그렇다면 그냥..
Invoke(AddressOf doWorkBehind) 라고 하면 안되나????
안된다, 우선 매개변수, iX 를 전달할 방법이 없다
그리고 또한 앞페이지에서 이야기 했지만 그림과 같이



Invoke메소드가 Delegate타입의 매개변수를 요구하고 있는 것이다
그렇다면 Delegate가 뭔지를 알아야 할 기회가 온것이다
Delegate는 단어의 의미는 대리하게 하다, 작업을 위임시키다,
그럼 무엇을 대리한다는 것인가??
Delegates 는 메소드를 참조하는 개체이다
아하..메소드, 함수나 프로시져를 참조하게 하는 개체로구나...
그런데 왜 이따위가 필요한 것이지???
예를 들어서, 콘트롤들을 크릭하거나 어떤 이벤트가 발생할때
우리는 이벤트프로시져에 하고 싶은 작업을 코딩한다
아래와 같이

Dim oQ As New Button
AddHandler oQ.Click, New EventHandler(AddressOf someProcedure)

EventHandler라고 하는 개체가 특수한 타입의 Delegate 개체인 것이다
다른 Delegate는 사용자가 정의하고 사용하지만
EventHandler는 시스템에서 특별히 제공하는 Delegate인 셈이다
조금씩 감이 오면 좋겠다..
위의 Invoke 메소드에서 사용한 매개변수나 EventHandler의 것과 유사하다

Invoke(New myWork(AddressOf doWorkBehind), iX)

바로 같은 성격의 일을 한것이다

***[LOG-IN]***

사용자가 정의하여 만드는 Delegate대신에 시스템에서 제공해주는 Delegate사용하기

아래와 같이 Delegate 개체를 하나 정의하고
프로시져로 사용할 함수 3개를 준비하고


Delegate Function myDel() As String

Function x()
	Return CLng(Rnd() * 100) + 100
End Function
Function y()
	Return CLng(Rnd() * 10000) + 10000
End Function
Function z()
	Return CLng(Rnd() * 1000000) + 1000000
End Function

버튼 3개를 사용하여 , 각각 다른 프로시져를 Delegate개체를 통하여 아래와 같이 부른다

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
	Me.TextBox1.Text = Invoke(New myDel(AddressOf x))
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
	Me.TextBox1.Text = Invoke(New myDel(AddressOf y))
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
	Me.TextBox1.Text = Invoke(New myDel(AddressOf z))
End Sub

위의 3개의 버튼에서 작업한 내용을 아래와 같이 할수 있다

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
	Me.TextBox1.Text = Invoke(New Func(Of String)(AddressOf x))
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
	Me.TextBox1.Text = Invoke(New Func(Of String)(AddressOf y))
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
	Me.TextBox1.Text = Invoke(New Func(Of String)(AddressOf z))
End Sub

어라..Func 라고 하는 Delegate를 선언한 적이 없는데..
어디서 온것이지????
이것은 위에서 우리가 사용하려고 선언한 Delegate 인 myDel을 사용하지 않은 것이다
Func 라고 하는 것은 시스템에서 제공해주는 Delegate인 것이다
즉 번거롭게 선언하고 사용하지 말고, 이런 것을 준비해놓을테니
필요할때 사용하라는 이야기다!!
위에서 이야기했던 이벤트프로시져를 사용하기 위하여 생성하는 개체 EventHandler 라고
하는 것도 시스템에서 제공해주는 특별한 Delegate라고 했었다
AddHandler oQ.Click, New EventHandler(AddressOf someProcedure)



그림과 같이 New 를 입력하고 차분하게 목록을 보면 Func 라는 항목이 나타난다
Func는 Function 함수를 표현하는 Delegate이고
Action은 Sub 프로시져를 표현하는 Delegate이다
시스템에서 제공해주는 Delegate인것이다
아이콘 모양이 그림과 같이 생긴것이 Delegate인 것이다
아니..그럼 처음부터 이것을 이야기 함 되지, 복잡하게 Delegate를 사용자가 정의하고
법썩을 떠는가??? 원리를 알아야 하니까..
Func(Of TResult) 라고 하는 것은 시스템에서 제공하는 것이니..
사용자가 어떤 타입의 결과를 받는 함수를 사용할지 모른다
사용자의 결정에 따라서 융통성있는 함수의 리턴값 타입의 결정이다
Action의 경우는 함수와 같이 Return값이 없으니 TResult는 없다

위의 경우는
Func(Of String) 이라고 New로 Delegate개체를 생성하겠다고 한것이다
결과값을 String값으로 받는 함수를 부르는 Delegate

***[LOG-IN]***

이번에는 아래의 그림과 같이
4개의 Thread를 만들어서 마음대로 다른 작업을 하면서
각각의 Thread는 독립적으로 실행되는 것을 만들어 보면서
Thread를 이해하고 활용하도록 하자



4개의 Thread를 만들어서
4개의 콘트롤 그룹이 독립적으로 일을 수행하도록 해보자
모두 별도의 Thread로 진행되니 각각 작업을 하면서 폼을 이리저리 움직일수 있고
체크박스와 텍스트박스도 당연히 체크하고,
텍스스박스에 값을 입력하기도 하고,
입력한 값을 Thread에서 읽은 값을 조건으로 작업을 제어하기도 하도록 하자

하나의 Main Thread로만 작업을 하면
어떤 콘트롤이 작업중에는 이 콘트롤에 부여된 작업이 끝나기 전까지는
아무일도 못한다
그러니 필요에 따라서 Thread를 만들어서 활용하는 것은
필수적인 일이다

콘트롤은 다양한 타입이 있다
Label도 있을 것이고,
CheckBox도 았을 것이고,
TextBox도 있을 것이고,
주어진 WindowForm의 콘트롤집합체(Controls)중에서 TextBox만 골라내고 싶다면
VBA에서는 TypeName같은 함수를 사용하여 알아내었다
VB.Net에서는 그런 구차한 짓없이 아래와 같이 원하는 타입의 콘트롤만 골라낸다

For Each oLabel As Label In oGroup.Controls.OfType(Of Label)()
    oLabel.Font = New Font("맑은 고딕", 16, FontStyle.Bold)
    oLabel.Text = "0"
Next

또 VBA에서는 아래와 같이 하여야 할 것을

If Me.CheckBoxThread4.Checked Then
    If ix = iLimit Then
        Me.ButtonThread4.Enabled = True
        Me.CheckBoxThread4.Enabled = True
        oThread1.Abort()
    End If
End If

VB.Net에서는 AndAlso와 같은 연산자가 있어서 아래와 같이 한줄로 표현한다

If Me.CheckBoxThread4.Checked AndAlso ix = iLimit Then
    Me.ButtonThread4.Enabled = True
    Me.CheckBoxThread4.Enabled = True
    oThread1.Abort()
End If

이것은
Me.CheckBoxThread4.Checked=True라는 논리식의 결과가 False 일때는
iX=iLimit 를 계산하지 않는다
하지만 VBA에서는
Me.CheckBoxThread4.checked=True와 iX=iLimit를 같이 계산한다
쓸데 없이 다음 계산을 할 필요없는데 하는 셈이다
또한 여러개의 논리식중 하나의 논리식에서 에러가 나면 어떤 논리식에서 에러가 났는지 찾아야 한다
하지만 AndAlso는 그런 것 또한 방지하니까..
AndAlso의 편리함이다
비슷한 연산자는 OrElse 라는 것이 있다
이것은 And가 아니고 Or일때 편리하게 사용하면 된다

Private Sub ButtonThread1_Click(sender As System.Object, e As System.EventArgs) Handles ButtonThread1.Click,
					ButtonThread2.Click, ButtonThread3.Click, ButtonThread4.Click
	Dim iLimit As Integer = 0

	CType(sender, Button).Enabled = False
	Select Case sender.name.ToString.Last
		Case "1"
			oThread1 = New Threading.Thread(AddressOf do1)
			TextBoxThread1.Enabled = False
			CheckBoxThread1.Enabled = False
			iLimit = CInt(Me.TextBoxThread1.Text)
			''Thread 에서 실행되는 프로시져에 매개변수를 전달한다
			oThread1.Start(iLimit)

		Case "2"
			...
			...
		Case "3"
			...
			...
		Case "4"
			...
			...

	End Select

End Sub

Sub do1(iLimit As Integer)
	For ix = 1 To 300
		Invoke(Sub()
		''Invoke 메소드의 매개변수로 Delegate를 사용할수도 있고
		''Lambda Expression으로 작성할수도 있는 것이 요점
			   Me.LabelThread1.Text = ix

			   If Me.CheckBoxThread1.Checked AndAlso ix = iLimit Then
				   Me.ButtonThread1.Enabled = True
				   Me.CheckBoxThread1.Enabled = True
				   '' Thread를 중단시키는 Abort메소드
				   oThread1.Abort()

			   End If
		   End Sub)
		Threading.Thread.Sleep(100)
	Next
	Me.ButtonThread1.Enabled = True
	Me.CheckBoxThread1.Enabled = True

End Sub
***[LOG-IN]***