q 메서드의 대리자
 
q 메서드를 보다 효율적으로 사용하기 위하여 특정 메서드 자체를 캡슐화할 있게 만들어 주는 방법

 

Delegate 주는 장점은 메서드 자체가 캡슐화 되어있기 때문에 메서드를 보다 효율적으로 사용할 있게 줍니다. 다음은 Delegate 간단한 구현입니다.

 

using System;

delegate void SimpleDelegate1();

delegate void SimpleDelegate2(int i);

class AType{

     public void F1() {

          System.Console.WriteLine("AType.F1");

     }

     public void F2(int x){

          System.Console.WriteLine("AType.F2 x=" + x);

     }

}

class DeleTest {

       public static void Main(){

          AType atype = new AType();

        SimpleDelegate1 s1 = new SimpleDelegate1(atype.F1);

        SimpleDelegate2 s2 = new SimpleDelegate2(atype.F2);

        s1();

        s2(1000);

    }//main

}//class

 

메서드를 캡슐화하여 사용할 있다는 것은 아주 단순한 기능입니다. 단순한 기능만큼이나 Delegate 파워는 굉장합니다. 다음과 같은 경우를 예를 들어보죠. 만약 하나의 클래스에서 다른 것은 필요 없고 단지 특정객체의 메서드에만 관심이 있다고 가정해보죠. 특정객체의 메서드는 객체의 멤버로 소속되어 있기 때문에 객체의 모든 기능을 사용할 있을 것입니다. 그런데 어떻게 메서드만을 빼올까라는 생각을 하게 것입니다.

 

C#에서 스레드를 실행한다는 것은 이와 비슷합니다. C#에서 사용되는 모든 스레드는 하나의 표준 스레드에 메서드만 추가 시켜주고 스레드를 시작하면 작업을 수행합니다. 만약 메서드 10개가 있다면 표준 스레드 10개를 만들고, 메서드를 표준 스레드에 하나씩 넣어주고 실행하라고 명령을 내리면 10개의 스레드가 동시에 작업을 수행하게 됩니다.

 

스레드와 메서드의 관계는 아주 단순합니다. Main() 메서드도 알고 보면 스레드입니다. 표준 스레드에 Main() 넣어주고 실행해 달라는 것이니까요. , 10개의 Main 동시에 돌린다고 가정해 보죠. 이럴 때는 10개의 표준 스레드가 필요하고 10개의 Main() 메서드를 각각 넣어준 실행을 하면 10개의 스레드는 CPU Time 분할해서 사용하게 되는 것입니다. 너한번 다른 너한번...... 그리고 마지막으로 나한번! , 표준 스레드 입장에서는 항상 스레드가 수행되어지기 전에 어떤 메서드가 들어올지 궁금해 것입니다. 이런 면에서 스레드는 다른 객체의 메서드만을 빼와서 수행하고자 것입니다. 스레드는 메서드를 Delegate 포장해서 메서드만을 달라고 요청하는 것입니다. 실제 스레드를 실행하기 위한 예는 다음과 같습니다.

 

n                  Top t = new Top();

n                  Thread  t = new Thread(new ThreadStart(t.메서드));

n                  thread.Start();

 

C#에서 등장하는 Delegate 스레드, 이벤트에서 아주 중요한 역할을 담당하고 있습니다. 메서드를 캡슐화하여 이용하고자 한다면 어디든 등장하는 놈이 바로 Delegate입니다. 특정 객체에서 메서드를 직접 호출하는 것보다 메서드의 표준틀을 이용하여 사용하는 것이 휠씬 쉬우니까요. 그리고 사실 Delegate C++에서 메서드의 주소호출에 해당합니다. 메서드의 특정 주소를 이용하여 메서드를 더욱 안전하고 쉽게 호출하겠다는 의미를 담고 있습니다. 그대로 Delegate 메서드의 대리자가 되는 것입니다.

 

 

출처 : 소설 같은 C#

Posted by 나비:D
:

delegate string GetTextCallback();
public string GetText()
{
    if (this.cb_DataSelectTime.InvokeRequired)
    {
        GetTextCallback callback = new GetTextCallback(GetText);
        return (string) this.Invoke(callback, new object[] { });
    }
    else
    {
        return this.cb_DataSelectTime.Text;
    }
}

delegate void SetTextCallback();
public string SetText()
{
    if (this.cb_DataSelectTime.InvokeRequired)
    {
        SetTextCallback callback = new GetTextCallback(SetText);
        return (string) this.Invoke(callback, new object[] { });
    }
    else
    {
        return this.cb_DataSelectTime.Text;
    }
}


일단 Set 과 Get형식의 델리게이트..

UI의 값을 참조시에는 Delegate와 INvokeRequired를 사용하여 호출 해주어야 한다.....

왜?? 아직 잘모르겠따 ㅡㅡ;;;;
생각보다 delegate가 먼말인지 모르겠돠 ㅡㅡ;;; 쟁장..

Posted by 나비:D
:

출처 : http://msdn2.microsoft.com/ko-kr/library/ms171728(VS.80).aspx

방법: 스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

Windows Forms 응용 프로그램의 성능 향상을 위해 다중 스레딩을 사용할 경우 스레드로부터 안전한 방식으로 컨트롤을 호출하도록 주의해야 합니다.

예제

Windows Forms 컨트롤에 대한 액세스는 기본적으로 스레드로부터 안전하지 않습니다. 컨트롤 상태를 조작하는 스레드가 두 개 이상 있는 경우 컨트롤이 일관성 없는 상태가 될 수 있습니다. 경합 상태, 교착 상태 등의 다른 스레드 관련 버그가 발생할 수 있습니다. 따라서 컨트롤에 대한 액세스가 스레드로부터 안전한 방식으로 수행되는지 확인해야 합니다.

.NET Framework에서는 사용자가 스레드로부터 안전하지 않은 방식으로 컨트롤에 액세스할 경우 이를 감지할 수 있습니다. 디버거에서 응용 프로그램을 실행할 때 컨트롤을 만든 스레드가 아닌 스레드에서 해당 컨트롤을 호출하려고 하면 디버거에서 InvalidOperationException을 발생시키고 "control name 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."라는 메시지를 표시합니다.

이 예외는 일부 환경에서 런타임에 디버깅하는 동안 안정적으로 발생합니다. 이 예외가 발생할 경우 문제를 수정하는 것이 좋습니다. 이 예외는 .NET Framework 2.0 이전 버전에서 작성한 응용 프로그램을 디버깅할 때 발생할 수 있습니다.

Note참고

CheckForIllegalCrossThreadCalls 속성 값을 false로 설정하여 이 예외를 비활성화할 수 있습니다. 그러면 컨트롤이 Visual Studio 2003에서와 같은 방식으로 실행됩니다.

다음 코드 예제에서는 작업자 스레드에서 스레드로부터 안전한 방식과 스레드로부터 안전하지 않은 방식으로 Windows Forms 컨트롤을 호출하는 방법을 보여 줍니다. 또한 스레드로부터 안전하지 않은 방식으로 TextBox 컨트롤의 Text 속성을 설정하는 방법과 스레드로부터 안전한 방식으로 Text 속성을 설정하는 두 가지 방법을 보여 줍니다.

Visual Basic
Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms

Public Class Form1
   Inherits Form
   
   ' This delegate enables asynchronous calls for setting
   ' the text property on a TextBox control.
   Delegate Sub SetTextCallback([text] As String)

   ' This thread is used to demonstrate both thread-safe and
   ' unsafe ways to call a Windows Forms control.
   Private demoThread As Thread = Nothing

   ' This BackgroundWorker is used to demonstrate the 
   ' preferred way of performing asynchronous operations.
   Private WithEvents backgroundWorker1 As BackgroundWorker

   Private textBox1 As TextBox
   Private WithEvents setTextUnsafeBtn As Button
   Private WithEvents setTextSafeBtn As Button
   Private WithEvents setTextBackgroundWorkerBtn As Button
   
   Private components As System.ComponentModel.IContainer = Nothing
   
   
   Public Sub New()
      InitializeComponent()
    End Sub
   
   
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso Not (components Is Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub
   
   
   ' This event handler creates a thread that calls a 
   ' Windows Forms control in an unsafe way.
    Private Sub setTextUnsafeBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextUnsafeBtn.Click

        Me.demoThread = New Thread( _
        New ThreadStart(AddressOf Me.ThreadProcUnsafe))

        Me.demoThread.Start()
    End Sub
   
   
   ' This method is executed on the worker thread and makes
   ' an unsafe call on the TextBox control.
   Private Sub ThreadProcUnsafe()
      Me.textBox1.Text = "This text was set unsafely."
   End Sub 

   ' This event handler creates a thread that calls a 
   ' Windows Forms control in a thread-safe way.
    Private Sub setTextSafeBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextSafeBtn.Click

        Me.demoThread = New Thread( _
        New ThreadStart(AddressOf Me.ThreadProcSafe))

        Me.demoThread.Start()
    End Sub
   
   
   ' This method is executed on the worker thread and makes
   ' a thread-safe call on the TextBox control.
   Private Sub ThreadProcSafe()
      Me.SetText("This text was set safely.")
    End Sub

   ' This method demonstrates a pattern for making thread-safe
   ' calls on a Windows Forms control. 
   '
   ' If the calling thread is different from the thread that
   ' created the TextBox control, this method creates a
   ' SetTextCallback and calls itself asynchronously using the
   ' Invoke method.
   '
   ' If the calling thread is the same as the thread that created
    ' the TextBox control, the Text property is set directly. 

    Private Sub SetText(ByVal [text] As String)

        ' InvokeRequired required compares the thread ID of the
        ' calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true.
        If Me.textBox1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.textBox1.Text = [text]
        End If
    End Sub

   ' This event handler starts the form's 
   ' BackgroundWorker by calling RunWorkerAsync.
   '
   ' The Text property of the TextBox control is set
   ' when the BackgroundWorker raises the RunWorkerCompleted
   ' event.
    Private Sub setTextBackgroundWorkerBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextBackgroundWorkerBtn.Click
        Me.backgroundWorker1.RunWorkerAsync()
    End Sub
   
   
   ' This event handler sets the Text property of the TextBox
   ' control. It is called on the thread that created the 
   ' TextBox control, so the call is thread-safe.
   '
   ' BackgroundWorker is the preferred way to perform asynchronous
   ' operations.
    Private Sub backgroundWorker1_RunWorkerCompleted( _
    ByVal sender As Object, _
    ByVal e As RunWorkerCompletedEventArgs) _
    Handles backgroundWorker1.RunWorkerCompleted
        Me.textBox1.Text = _
        "This text was set safely by BackgroundWorker."
    End Sub

   #Region "Windows Form Designer generated code"
   
   
   Private Sub InitializeComponent()
      Me.textBox1 = New System.Windows.Forms.TextBox()
      Me.setTextUnsafeBtn = New System.Windows.Forms.Button()
      Me.setTextSafeBtn = New System.Windows.Forms.Button()
      Me.setTextBackgroundWorkerBtn = New System.Windows.Forms.Button()
      Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker()
      Me.SuspendLayout()
      ' 
      ' textBox1
      ' 
      Me.textBox1.Location = New System.Drawing.Point(12, 12)
      Me.textBox1.Name = "textBox1"
      Me.textBox1.Size = New System.Drawing.Size(240, 20)
      Me.textBox1.TabIndex = 0
      ' 
      ' setTextUnsafeBtn
      ' 
      Me.setTextUnsafeBtn.Location = New System.Drawing.Point(15, 55)
      Me.setTextUnsafeBtn.Name = "setTextUnsafeBtn"
      Me.setTextUnsafeBtn.TabIndex = 1
      Me.setTextUnsafeBtn.Text = "Unsafe Call"
      ' 
      ' setTextSafeBtn
      ' 
      Me.setTextSafeBtn.Location = New System.Drawing.Point(96, 55)
      Me.setTextSafeBtn.Name = "setTextSafeBtn"
      Me.setTextSafeBtn.TabIndex = 2
      Me.setTextSafeBtn.Text = "Safe Call"
      ' 
      ' setTextBackgroundWorkerBtn
      ' 
      Me.setTextBackgroundWorkerBtn.Location = New System.Drawing.Point(177, 55)
      Me.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn"
      Me.setTextBackgroundWorkerBtn.TabIndex = 3
      Me.setTextBackgroundWorkerBtn.Text = "Safe BW Call"
      ' 
      ' backgroundWorker1
      ' 
      ' 
      ' Form1
      ' 
      Me.ClientSize = New System.Drawing.Size(268, 96)
      Me.Controls.Add(setTextBackgroundWorkerBtn)
      Me.Controls.Add(setTextSafeBtn)
      Me.Controls.Add(setTextUnsafeBtn)
      Me.Controls.Add(textBox1)
      Me.Name = "Form1"
      Me.Text = "Form1"
      Me.ResumeLayout(False)
      Me.PerformLayout()
   End Sub 'InitializeComponent 
   
   #End Region
   
   <STAThread()>  _
   Shared Sub Main()
      Application.EnableVisualStyles()
      Application.Run(New Form1())
    End Sub
End Class
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace CrossThreadDemo
{
    public class Form1 : Form
    {
        // This delegate enables asynchronous calls for setting
        // the text property on a TextBox control.
        delegate void SetTextCallback(string text);

        // This thread is used to demonstrate both thread-safe and
        // unsafe ways to call a Windows Forms control.
        private Thread demoThread = null;

        // This BackgroundWorker is used to demonstrate the 
        // preferred way of performing asynchronous operations.
        private BackgroundWorker backgroundWorker1;

        private TextBox textBox1;
        private Button setTextUnsafeBtn;
        private Button setTextSafeBtn;
        private Button setTextBackgroundWorkerBtn;

        private System.ComponentModel.IContainer components = null;

        public Form1()
        {
            InitializeComponent();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        // This event handler creates a thread that calls a 
        // Windows Forms control in an unsafe way.
        private void setTextUnsafeBtn_Click(
            object sender, 
            EventArgs e)
        {
            this.demoThread = 
                new Thread(new ThreadStart(this.ThreadProcUnsafe));

            this.demoThread.Start();
        }

        // This method is executed on the worker thread and makes
        // an unsafe call on the TextBox control.
        private void ThreadProcUnsafe()
        {
            this.textBox1.Text = "This text was set unsafely.";
        }

        // This event handler creates a thread that calls a 
        // Windows Forms control in a thread-safe way.
        private void setTextSafeBtn_Click(
            object sender, 
            EventArgs e)
        {
            this.demoThread = 
                new Thread(new ThreadStart(this.ThreadProcSafe));

            this.demoThread.Start();
        }

        // This method is executed on the worker thread and makes
        // a thread-safe call on the TextBox control.
        private void ThreadProcSafe()
        {
            this.SetText("This text was set safely.");
        }

        // This method demonstrates a pattern for making thread-safe
        // calls on a Windows Forms control. 
        //
        // If the calling thread is different from the thread that
        // created the TextBox control, this method creates a
        // SetTextCallback and calls itself asynchronously using the
        // Invoke method.
        //
        // If the calling thread is the same as the thread that created
        // the TextBox control, the Text property is set directly. 

        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {    
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.textBox1.Text = text;
            }
        }

        // This event handler starts the form's 
        // BackgroundWorker by calling RunWorkerAsync.
        //
        // The Text property of the TextBox control is set
        // when the BackgroundWorker raises the RunWorkerCompleted
        // event.
        private void setTextBackgroundWorkerBtn_Click(
            object sender, 
            EventArgs e)
        {
            this.backgroundWorker1.RunWorkerAsync();
        }
        
        // This event handler sets the Text property of the TextBox
        // control. It is called on the thread that created the 
        // TextBox control, so the call is thread-safe.
        //
        // BackgroundWorker is the preferred way to perform asynchronous
        // operations.

        private void backgroundWorker1_RunWorkerCompleted(
            object sender, 
            RunWorkerCompletedEventArgs e)
        {
            this.textBox1.Text = 
                "This text was set safely by BackgroundWorker.";
        }

        #region Windows Form Designer generated code

        private void InitializeComponent()
        {
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.setTextUnsafeBtn = new System.Windows.Forms.Button();
            this.setTextSafeBtn = new System.Windows.Forms.Button();
            this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
            this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
            this.SuspendLayout();
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 12);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(240, 20);
            this.textBox1.TabIndex = 0;
            // 
            // setTextUnsafeBtn
            // 
            this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55);
            this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
            this.setTextUnsafeBtn.TabIndex = 1;
            this.setTextUnsafeBtn.Text = "Unsafe Call";
            this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
            // 
            // setTextSafeBtn
            // 
            this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55);
            this.setTextSafeBtn.Name = "setTextSafeBtn";
            this.setTextSafeBtn.TabIndex = 2;
            this.setTextSafeBtn.Text = "Safe Call";
            this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
            // 
            // setTextBackgroundWorkerBtn
            // 
            this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55);
            this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
            this.setTextBackgroundWorkerBtn.TabIndex = 3;
            this.setTextBackgroundWorkerBtn.Text = "Safe BW Call";
            this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
            // 
            // backgroundWorker1
            // 
            this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(268, 96);
            this.Controls.Add(this.setTextBackgroundWorkerBtn);
            this.Controls.Add(this.setTextSafeBtn);
            this.Controls.Add(this.setTextUnsafeBtn);
            this.Controls.Add(this.textBox1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion


        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new Form1());
        }

    }
}

스레드로부터 안전하지 않은 Windows Forms 컨트롤 호출

스레드로부터 안전하지 않은 Windows Forms 컨트롤을 호출하는 방법은 작업자 스레드에서 직접 호출하는 것입니다. 응용 프로그램을 디버깅할 때 디버거에서는 InvalidOperationException을 발생시키고 컨트롤 호출이 스레드로부터 안전하지 않다는 경고를 표시합니다.

Visual Basic
' This event handler creates a thread that calls a 
' Windows Forms control in an unsafe way.
 Private Sub setTextUnsafeBtn_Click( _
 ByVal sender As Object, _
 ByVal e As EventArgs) Handles setTextUnsafeBtn.Click

     Me.demoThread = New Thread( _
     New ThreadStart(AddressOf Me.ThreadProcUnsafe))

     Me.demoThread.Start()
 End Sub


' This method is executed on the worker thread and makes
' an unsafe call on the TextBox control.
Private Sub ThreadProcUnsafe()
   Me.textBox1.Text = "This text was set unsafely."
End Sub 
// This event handler creates a thread that calls a 
// Windows Forms control in an unsafe way.
private void setTextUnsafeBtn_Click(
    object sender, 
    EventArgs e)
{
    this.demoThread = 
        new Thread(new ThreadStart(this.ThreadProcUnsafe));

    this.demoThread.Start();
}

// This method is executed on the worker thread and makes
// an unsafe call on the TextBox control.
private void ThreadProcUnsafe()
{
    this.textBox1.Text = "This text was set unsafely.";
}

스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

스레드로부터 안전한 방식으로 Windows Forms 컨트롤을 호출하려면

  1. 컨트롤의 InvokeRequired 속성을 쿼리합니다.

  2. InvokeRequiredtrue를 반환하는 경우에는 컨트롤을 실제로 호출하는 대리자를 사용하여 Invoke를 호출합니다.

  3. InvokeRequiredfalse를 반환하는 경우에는 컨트롤을 직접 호출합니다.

다음 코드 예제에서는 SetText라는 유틸리티 메서드에서 이 논리를 구현합니다. SetTextDelegate 대리자 형식은 SetText 메서드를 캡슐화합니다. TextBox 컨트롤의 InvokeRequiredtrue를 반환하면 SetText 메서드는 SetTextDelegate 인스턴스를 만들고 폼의 Invoke 메서드를 호출합니다. 그러면 TextBox 컨트롤을 만든 스레드에서 SetText 메서드가 호출되고 이 스레드 컨텍스트에서 Text 속성이 직접 설정됩니다.

Visual Basic
' This event handler creates a thread that calls a 
' Windows Forms control in a thread-safe way.
 Private Sub setTextSafeBtn_Click( _
 ByVal sender As Object, _
 ByVal e As EventArgs) Handles setTextSafeBtn.Click

     Me.demoThread = New Thread( _
     New ThreadStart(AddressOf Me.ThreadProcSafe))

     Me.demoThread.Start()
 End Sub


' This method is executed on the worker thread and makes
' a thread-safe call on the TextBox control.
Private Sub ThreadProcSafe()
   Me.SetText("This text was set safely.")
 End Sub
// This event handler creates a thread that calls a 
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(
    object sender, 
    EventArgs e)
{
    this.demoThread = 
        new Thread(new ThreadStart(this.ThreadProcSafe));

    this.demoThread.Start();
}

// This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
    this.SetText("This text was set safely.");
}
Visual Basic
' This method demonstrates a pattern for making thread-safe
' calls on a Windows Forms control. 
'
' If the calling thread is different from the thread that
' created the TextBox control, this method creates a
' SetTextCallback and calls itself asynchronously using the
' Invoke method.
'
' If the calling thread is the same as the thread that created
 ' the TextBox control, the Text property is set directly. 

 Private Sub SetText(ByVal [text] As String)

     ' InvokeRequired required compares the thread ID of the
     ' calling thread to the thread ID of the creating thread.
     ' If these threads are different, it returns true.
     If Me.textBox1.InvokeRequired Then
         Dim d As New SetTextCallback(AddressOf SetText)
         Me.Invoke(d, New Object() {[text]})
     Else
         Me.textBox1.Text = [text]
     End If
 End Sub
// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control. 
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly. 

private void SetText(string text)
{
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.textBox1.InvokeRequired)
    {    
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
    {
        this.textBox1.Text = text;
    }
}

BackgroundWorker를 사용하여 스레드로부터 안전한 방식으로 호출

응용 프로그램에서 다중 스레딩을 구현하는 기본 방법은 BackgroundWorker 구성 요소를 사용하는 것입니다. BackgroundWorker 구성 요소는 다중 스레딩의 이벤트 구동 모델을 사용합니다. 작업자 스레드는 DoWork 이벤트 처리기를 실행하고 컨트롤을 만드는 스레드는 ProgressChangedRunWorkerCompleted 이벤트 처리기를 실행합니다. DoWork 이벤트 처리기에서 컨트롤을 호출하지 마십시오.

다음 코드 예제에는 비동기적으로 수행되는 작업이 없으므로 DoWork 이벤트 처리기 구현이 없습니다. TextBox 컨트롤의 Text 속성은 RunWorkerCompleted 이벤트 처리기에서 직접 설정됩니다.

Visual Basic
' This event handler starts the form's 
' BackgroundWorker by calling RunWorkerAsync.
'
' The Text property of the TextBox control is set
' when the BackgroundWorker raises the RunWorkerCompleted
' event.
 Private Sub setTextBackgroundWorkerBtn_Click( _
 ByVal sender As Object, _
 ByVal e As EventArgs) Handles setTextBackgroundWorkerBtn.Click
     Me.backgroundWorker1.RunWorkerAsync()
 End Sub


' This event handler sets the Text property of the TextBox
' control. It is called on the thread that created the 
' TextBox control, so the call is thread-safe.
'
' BackgroundWorker is the preferred way to perform asynchronous
' operations.
 Private Sub backgroundWorker1_RunWorkerCompleted( _
 ByVal sender As Object, _
 ByVal e As RunWorkerCompletedEventArgs) _
 Handles backgroundWorker1.RunWorkerCompleted
     Me.textBox1.Text = _
     "This text was set safely by BackgroundWorker."
 End Sub
// This event handler starts the form's 
// BackgroundWorker by calling RunWorkerAsync.
//
// The Text property of the TextBox control is set
// when the BackgroundWorker raises the RunWorkerCompleted
// event.
private void setTextBackgroundWorkerBtn_Click(
    object sender, 
    EventArgs e)
{
    this.backgroundWorker1.RunWorkerAsync();
}

// This event handler sets the Text property of the TextBox
// control. It is called on the thread that created the 
// TextBox control, so the call is thread-safe.
//
// BackgroundWorker is the preferred way to perform asynchronous
// operations.

private void backgroundWorker1_RunWorkerCompleted(
    object sender, 
    RunWorkerCompletedEventArgs e)
{
    this.textBox1.Text = 
        "This text was set safely by BackgroundWorker.";
}

Windows Forms의 ActiveX 컨트롤

폼에서 ActiveX 컨트롤을 사용하는 경우 디버거에서 실행할 때 크로스 스레드 InvalidOperationException이 발생할 수 있습니다. 이런 경우에는 ActiveX 컨트롤에서 다중 스레딩을 지원하지 않습니다. Windows Forms에서의 ActiveX 컨트롤 사용에 대한 자세한 내용은 Windows Forms 및 관리되지 않는 응용 프로그램을 참조하십시오.

Visual Studio를 사용하는 경우에는 Visual Studio 호스팅 프로세스를 비활성화하여 이 예외가 발생하지 않도록 만들 수 있습니다.

자세한 내용은 다음을 참조하십시오. 방법: 호스팅 프로세스 비활성화.

강력한 프로그래밍

Caution note주의

어떤 종류의 다중 스레딩을 사용하든지 코드가 매우 심각하고 복잡한 버그에 노출될 수 있습니다. 다중 스레딩을 사용하는 솔루션을 구현하기 전에 관리되는 스레딩을 구현하는 최선의 방법에서 자세한 내용을 참조하십시오.

참고 항목

Posted by 나비:D
:

- 메서드 대리자 델리게이트(delegate)

001: using System;
002: //델리게이트 선언
003: delegate void dele1();
004: delegate int dele2(int a,int b);
005: class MainClass
006: {
007:    static void Main(string[] args)
008:    {
009:           //Math클래스 선언및 인스턴스화
010:           MathClass MathClass=new MathClass();
011:           //델리게이트 선언및 인스턴스화
012:           dele1 Sum1=new dele1(MathClass.Intro);
013:           dele2 Sum2=new dele2(MathClass.Sum);
014:           //델리게이트에 위임된 메서드 실행
015:           Sum1();
016:           Console.WriteLine("Result : {0}",Sum2(-10,90));
017:    }
018: }
019: class MathClass
020: {
021:    public void Intro()
022:    {
023:           Console.WriteLine("계산을 시작합니다.");
024:    }
025:    public int Sum(int a, int b)
026:    {
027:           return a+b;
028:    }
029: }

 

001: using System;
002: delegate void deleMath(int Value); //델리게이트 선언
003: class MainClass
004: {
005: static void Main(string[] args)
006: {
007: //Math클래스 선언및 인스턴스화
008: MathClass MathClass=new MathClass();
009:
010: //델리게이트 선언및 인스턴스화(덧셈)
011: deleMath Math=new deleMath(MathClass.Plus);
012:
013: //위임연산(뺄셈,곱셈추가)
014: Math+=new deleMath(MathClass.Minus);
015: Math+=new deleMath(MathClass.Multiply);
016: //결과1
017: MathClass.Number=10;
018: Math(10);
019: Console.WriteLine("Result:{0}",MathClass.Number);
020:
021: //위임연산(뺄셈제거)
022: Math-=new deleMath(MathClass.Minus);
023: //결과2
024: MathClass.Number=10;
025: Math(10);
026: Console.WriteLine("Result:{0}",MathClass.Number);
027:
028: //위임연산(곱셈제거)
029: Math-=new deleMath(MathClass.Multiply);
030: //결과3
031: MathClass.Number=10;
032: Math(10);
033: Console.WriteLine("Result:{0}",MathClass.Number);
034:
035: }
036: }
037: class MathClass
038: {
039: public int Number;
040: public void Plus(int Value)//더하기
041: {
042: Number+=Value;
043: }
044: public void Minus(int Value)//빼기
045: {
046: Number-=Value;
047: }
048: public void Multiply(int Value)//곱하기
049: {
050: Number*=Value;
051: }
052: }

Posted by 나비:D
:

C# :: Tcp Chat

2008. 3. 19. 16:00

출처 : http://www.java2s.com/Code/CSharp/Network/NewTcpChat.htm

New Tcp Chat


/*
C# Network Programming 
by Richard Blum
Publisher: Sybex 
ISBN: 0782141765
*/
using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
public class NewTcpChat : Form
{
private static TextBox newText;
private static ListBox results;
private static ListBox hosts;
private static Socket client;
private static byte[] data = new byte[1024];
public NewTcpChat()
{
Text = "New TCP Chat Program";
Size = new Size(400, 380);
Label label1 = new Label();
label1.Parent = this;
label1.Text = "Enter text string:";
label1.AutoSize = true;
label1.Location = new Point(10, 30);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
results = new ListBox();
results.Parent = this;
results.Location = new Point(10, 85);
results.Size = new Size(360, 10 * Font.Height);
Label label2 = new Label();
label2.Parent = this;
label2.Text = "Active hosts";
label2.AutoSize = true;
label2.Location = new Point(10, 240);
hosts = new ListBox();
hosts.Parent = this;
hosts.Location = new Point(10, 255);
hosts.Size = new Size(360, 5 * Font.Height);
Button sendit = new Button();
sendit.Parent = this;
sendit.Text = "Send";
sendit.Location = new Point(220,52);
sendit.Size = new Size(5 * Font.Height, 2 * Font.Height);
sendit.Click += new EventHandler(ButtonSendOnClick);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
Button listen = new Button();
listen.Parent = this;
listen.Text = "Listen";
listen.Location = new Point(295,52);
listen.Size = new Size(6 * Font.Height, 2 * Font.Height);
listen.Click += new EventHandler(ButtonListenOnClick);
Thread fh = new Thread(new ThreadStart(findHosts));
fh.IsBackground = true;
fh.Start();
}
void ButtonListenOnClick(object obj, EventArgs ea)
{
results.Items.Add("Listening for a client...");
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
newsock.Bind(iep);
newsock.Listen(5);
newsock.BeginAccept(new AsyncCallback(AcceptConn), newsock);
Thread advertise = new Thread(new ThreadStart(srvrAdvertise));
advertise.IsBackground = true;
advertise.Start();
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
results.Items.Add("Connecting...");
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
string selectedhost = (string)hosts.SelectedItem;
string[] hostarray = selectedhost.Split(':');
IPEndPoint iep = new IPEndPoint(IPAddress.Parse(hostarray[1]), 9050);
client.BeginConnect(iep, new AsyncCallback(Connected), client);
}
void ButtonSendOnClick(object obj, EventArgs ea)
{
byte[] message = Encoding.ASCII.GetBytes(newText.Text);
newText.Clear();
client.BeginSend(message, 0, message.Length, 0, new AsyncCallback(SendData), client);
}
void AcceptConn(IAsyncResult iar)
{
Socket oldserver = (Socket)iar.AsyncState;
client = oldserver.EndAccept(iar);
results.Items.Add("Connection from: " + client.RemoteEndPoint.ToString());
Thread receiver = new Thread(new ThreadStart(ReceiveData));
receiver.IsBackground = true;
receiver.Start();
}
void Connected(IAsyncResult iar)
{
try
{
client.EndConnect(iar);
results.Items.Add("Connected to: " + client.RemoteEndPoint.ToString());
Thread receiver = new Thread(new ThreadStart(ReceiveData));
receiver.IsBackground = true;
receiver.Start();
} catch (SocketException)
{
results.Items.Add("Error connecting");
}
}
void SendData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
}
void ReceiveData()
{
int recv;
string stringData;
while (true)
{
recv = client.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
if (stringData == "bye")
break;
results.Items.Add(stringData);
}
stringData = "bye";
byte[] message = Encoding.ASCII.GetBytes(stringData);
client.Send(message);
client.Close();
results.Items.Add("Connection stopped");
return;
}
void srvrAdvertise()
{
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, 9051);
byte[] hostname = Encoding.ASCII.GetBytes(Dns.GetHostName());
while (true)
{
server.SendTo(hostname, iep);
Thread.Sleep(60000);
}
}
void findHosts()
{
while(true)
{
Socket remoteHosts = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9051);
EndPoint ep = (EndPoint)iep;
remoteHosts.Bind(iep);
byte[] data = new byte[1024];
int recv = remoteHosts.ReceiveFrom(data, ref ep);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
string entry = stringData + ":" + ep.ToString();
if (!hosts.Items.Contains(entry))
hosts.Items.Add(entry);
}
}
public static void Main()
{
Application.Run(new NewTcpChat());
}
}

Posted by 나비:D
:

소켓통신으로 객체를 마샬링하여 통째로 전달하여 보자.

우리가 소켓통신을 사용하다 보면 배열이나 구조체를 통째로

날리면 편하겠다 라는 생각을 많이 하게 된다.

먼저 우리가 알아두어야 할 개념이 있다.

MarshalAsAttribute의 설명

이 특성은 매개 변수, 필드 또는 반환 값에 적용될 수 있습니다.

각 데이터 형식에 기본 마샬링 동작이 있으므로 이 특성은 선택적입니다. 이 특성은 주어진 형식을 여러 형식으로 마샬링할 수 있는 경우에만 필요합니다. 예를 들어, 문자열은 LPStr, LPWStr, LPTStr 또는 BStr로 관리되지 않는 코드로 마샬링할 수 있습니다. 기본적으로 공용 언어 런타임에서는 문자열 매개 변수를 BStr로 COM 메서드로 마샬링합니다. MarshalAsAttribute 속성을 개별 필드나 매개 변수에 적용하여 특정 문자열이 BStr 대신 LPStr로 마샬링되도록 할 수 있습니다. 형식 라이브러리 내보내기(Tlbexp.exe)는 사용자의 마샬링 기본 설정을 공용 언어 런타임에 전달합니다.

일부 매개 변수 및 반환 값은 COM interop 또는 플랫폼 호출과 함께 사용할 경우 기본 마샬링 동작이 다릅니다. 기본적으로 런타임에서는 문자열 매개 변수 및 값 형식의 필드를 LPStr로 플랫폼 호출 메서드 또는 함수로 마샬링합니다. 자세한 내용은 기본 마샬링 동작을 참조하십시오.

대부분의 경우 이 특성은 다음 C# 시그니처와 같이 UnmanagedType 열거형을 사용하는 관리되지 않는 데이터의 형식을 쉽게 식별합니다.

void MyMethod([MarshalAs(LPStr)] String s);

일부 UnmanagedType 열거형 멤버에는 추가 정보가 필요합니다. 예를 들어, UnmanagedTypeLPArray 일 때에는 추가 정보가 필요합니다. 이 특성을 배열에 사용하는 방법에 대한 자세한 내용은배열에 대한 기본 마샬링을 참조하십시오.

형식 라이브러리 가져오기(Tlbimp.exe)는 이 특성을 매개 변수, 필드 및 반환 값에도 적용하여 입력 형식 라이브러리의 데이터 형식이 해당 관리되는 데이터 형식의 기본 형식이 아님을 나타냅니다. Tlbimp.exe는 입력 형식 라이브러리에 지정된 형식에 상관없이 명확함을 기하기 위해 StringObject 형식에 항상 MarshalAsAttribute를 적용합니다.


예제를 보자.


[C#]

//Applied to a parameter.

  public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);

//Applied to a field within a class.

  class MsgText {

    [MarshalAs(UnmanagedType.LPWStr)] Public String msg;

  }

//Applied to a return value.

[return: MarshalAs(UnmanagedType.LPWStr)]

public String GetMessage();

 

이 마샬링의 개념이 정리가 되었으면 이제  관리되지 않는 메모리 블록의 데이터를

관리되는 개체로 마샬링하는 메서드를 정리 해보도록 하자.


Marshal.PtrToStructure 메서드사용 예제

UCOMITypeInfo typeInfo = ...;

IntPtr ptr = IntPtr.Zero;

typeInfo.GetTypeAttr(ref ptr);

TYPEATTR attr = (TYPEATTR)Marshal.PtrToStructure(ptr,

    typeof(TYPEATTR));





실전 객체 전달 프로그래밍


using System.Runtime.InteropServices; //관리되지않는 코드 (포인터 사용및 구조체를 시퀜셜하게 만들기위해)

using System.Text;

using System.Net.Sockets;

using System.Diagnostics;  


// 클래스(구조체) 샘플.

[StructLayout(LayoutKind.Sequential)]

public class TempPacket

{

    public byte Command;

    public byte Version;

    public ushort Length;        

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)] public byte[] Data;  //바이트 배열....

}


public class Sample

{

    public void Text()

    {

        TempPacket tmpPacket = new TempPacket();


        tmpPacket.Command = 0x10;

        tmpPacket.Version = 0x01;

        tmpPacket.Length = 10;

        tmpPacket.Data = Encoding.ASCII.GetBytes("0123456789");


        //객체를 보내보자.                    

        byte[] buffer = new byte[Marshal.SizeOf(tmpPacket)];

        unsafe

        {

            fixed(byte* fixed_buffer = buffer)

            {

                Marshal.StructureToPtr(tmpPacket, (IntPtr)fixed_buffer, false);

            }

        }

   

        //소켓열고        

        TcpClient tcpClient = new TcpClient();

        tcpClient.Connect("localhost", 8296);

   

        NetworkStream networkStream = tcpClient.GetStream();

   

        networkStream.Write(buffer, 0, Marshal.SizeOf(tmpPacket));

               

        //객체받고

        TempPacket tmpPacket2 = new TempPacket();

        networkStream.Read(buffer, 0, Marshal.SizeOf(tmpPacket));

        unsafe

        {

            fixed(byte* fixed_buffer = buffer)

            {

                Marshal.PtrToStructure((IntPtr)fixed_buffer, tmpPacket2);

            }

        }    

        string data;

   

        data = Encoding.ASCII.GetString(tmpPacket2.Data, 0, 10);

        Debug.WriteLine(data);

        networkStream.Close();

        tcpClient.Close();

    }

}


 


  작성자 : HOONS(박경훈)
  이메일 : tajopkh@hanmail.net
  홈페이지 : http://www.hoonsbara.com 

Posted by 나비:D
:

이번에는 우리가 가지고 있는 객체와 클래스의 기초 지식을 바탕으로 가상 모델링 해보도록 하겠습니다. 가상 모델링으로 구축 할 시스템은 바로 가상 축구 게임 입니다. 이제부터 클래스를 정의하고 동작(메서드)을 정의하는 요령을 익혀 보도록 합시다.

1. 일단 게임에 필요한 대상이나 동작 같은 요소들을 모두 적습니다.

사람, 공격수, 슛, 패스, 골키퍼, 펀칭, 태클, 축구공, 전반전, 팀, 선수, 수비수, 심판, 파울, 경기시간, 후반전 등등

2. 적어 놓은 요소들 중에는 동사와 명사가 있습니다. 명사는 바로 클래스가 되는 것이고 동사는 바로 메서드가 되는 것입니다. 이제 클래스와 메서드를 구분해 봅시다.

클래스(명사) - 사람, 공격수, 골키퍼, 축구공, 전반전, 팀, 선수, 수비수, 심판, 경기시간, 후반전 등등

메서드(동사) - 슛, 패스, 펀칭, 태클 등등

3. 다음은 클래스 안의 메서드를 정리 합니다.

4. 설계 된 클래스들의 연관을 살펴 보고, 상속과 추상 or 다형성의 개념을 도입합니다. 이번 단계는 앞서 우리가 공부한 객체 지향의 설계 방법을 활용할 수 있는 단계입니다.


처음 단순하게 늘어 놓았던 단어들이 한 단계, 한 단계 정리 되면서 제법 폼 있는 설계도가 완성 되었습니다. 클래스 모델링이라고 해서 절대 대단한 것이 있는 것이 아닙니다. 누구나 쉽게 설계하고 모델링 할 수 있습니다. 지금 언급한 4가지 단계는 필자의 필살기적 노하우를 정리 해 본 것입니다. 반드시 기억해 두시기 바랍니다.

우리가 게임이든 어떤 소프트웨어든 프로그래밍 하기 전에 가장 먼저 해야 할 중요한 작업이 클래스 모델링의 과정입니다. 체계적인 뼈대를 갖추고 프로그래밍을 하는 것과 뼈대를 세워 가면서 프로그래밍을 하는 것은 확연히 차이가 나게 됩니다.

앞서 말했듯이 객체 지향적 사고는 한번에 자신의 머리 속에 정착 되는 것은 아닙니다. 반드시 직접 설계해보고 토론해보고 또 생각해보는 반복적인 노력을 하시길 바랍니다. 그럼 어느새 내가 만든 프로그램은 뼈대가 생기게 될 것이고 더욱더 논리적인 프로그램을 만들 수 있을 것입니다.

작성자 : HOONS(박경훈)
이메일 : tajopkh@hanmail.net
홈페이지 : http://www.hoonsbara.com

Posted by 나비:D
:

1. Introduction
저번 강좌에서는 객체와 클래스의 개념에 대해서 자세하게 알아 보았습니다. 이제 어느 정도 객체와 클래스의 개념을 이해 했을 것입니다. 하지만 이런 객체와 클래스를 알게 되었다고 해서 우리가 객체 지향 적인 프로그래밍을 설계하고 활용 할 수 있는 것은 아닙니다. 이 개념은 꾸준히 연구하고 설계하는 노력에서 나오는 것이기 때문입니다. 그래서 이번 장에서 클래스를 설계하는 방법에 대해 연구해보고 설계하는 방법을 다루어 볼 것입니다. 이번 장의 대상은 객체와 클래스의 개념을 이해 하고 있으신 분 이여야 하며 클래스의 문법 또한 이해하고 있어야 합니다. 그럼 객체 지향 설계에 들어 가보도록 하겠습니다.

2. 객체지향의 단계적 활용

2.1 업무 요청

여러분은 어느 호텔의 고객정보관련 프로그램 일을 유지하고 개발하는 개발자입니다. 어느 무료한 시간을 보내고 있던 중에 새로운 작업요청이 들어 왔습니다.

To. 고객관리 개발자

현재 호텔에 투숙하고 있는 고객의 정보를 가지고 있다가 투숙하고 있는 고객들의 성(gender)을 물어보면 그 고객이 남자인지 여자인지 반환해 주는 프로그램을 만들어 주세요

아주 간단한 프로그램을 요청이 들어왔네요. 작업내용을 한번 정리 해보겠습니다. 호텔의 고객들의 성을 반환해 주는 프로그램을 만들어 주면 됩니다. 근데 잠시 저장 시켰다가 요청한 아이의 성을 반환해 주라는 것이군요. 머 어려울 거 없는 내용입니다.

위와 같은 업무를 받았을 때 여러분은 클래스를 어떻게 설계하고 정의할지 생각해 보시기 바랍니다. 그리고 이제 단계별로 객체지향 적인 설계를 해 볼 것입니다. 여러분이 생각하고 활용하는 객체 지향의 능력은 어느 정도 되는지도 한번 점검해 보기 바랍니다.

2.2 객체 지향의 30% 활용

public class Custom
{
//여러 고객정보들 생략
private bool Sung; //true=남,false=여
public Custom(bool _Sung) //고객의 성을 저장하는 생성자
{
Sung=_Sung;
}
public string GetSung
{
get{return Sung;}
set{Sung=value;}
}
public string GetSung() // 고객의 성을 반환해주는 메소드
{
if(Sung)
return "남자";
else
return "여자";
}
}

객체를 30% 활용한 코드를 살펴 봅시다. 고객의 정보를 담을 Custom이라는 클래스를 만들었습니다. 그리고 Sung이라는 변수를 이용해서 남자 고객인지 여자 고객인지 구분하여 문자열을 전달합니다. 어떻습니까? 별 문제가 없어 보이죠? 대부분의 개발자들이 위와 같이 클래스를 설계 할 것입니다.

하지만 위의 클래스 객체 지향적인 설계로 보여지지 않습니다. 위의 프로그램 설계는 남자 고객인지 여자 고객인지 확인 하기 위해서는 생성한 객체 Sung이라는 변수를 확인 해야만 합니다. 다시 말하면 남자고객과 여자고객 두 개의 객체를 하게 되면 사실상 남자 고객인지 여자 고객인지 명확하게 구별 되지 못하는 거입니다. 이제 다음 단계를 살펴 봅시다. 그럼 왜 효율적이지 못한 것인지 충분히 느끼게 될 것입니다.

2.3 객체 지향의 60% 활용

public class Custom
{
// 고객 정보들 생략!
}
public class Man : Custom
{
public string GetSung()
{
return "남자";
}
}
public class Woman : Custom
{
public string GetSung()
{
return "여자";
}
}

이번에 설계된 클래스는 고객 객체를 생성 할 때 너는 여자다 남자다 라는 인자를 기억해둘 필요가 없습니다. 고객 클래스에서 Sung인자를 없애버렸습니다. 프로그램 메모리도 어느 정도 절약 할 수 있게 된 것이죠. 그리고 남자와 여자라는 자식 클래스를 별도로 만들었습니다.

이 클래스는 너의 성이 무엇이냐 라는 질문에 어떤 플래그를 거치지 않고 자신의 성을 말할 수 있습니다. 여기서는 객체 지향의 상속성 이라는 개념을 도입한 것입니다. 너무나 중요한 개념입니다. 상속을 도입했다고 해서 첫술에 배부르면 안 됩니다. 자, 그럼 이제 다형성과, 추상화의 개념을 도입하여 클래스를 설계 해 보겠습니다.


2.4
객체 지향의 90% 활용

public abstract class Custom
{
abstract public string GetSung(); //추상화
}
public class Man : Custom
{
public override string GetSung() //다형성
{
return "남자";
}
}
public class Woman : Custom
{
public override string GetSung()
{
return "여자";
}
}

추상화의 개념은 Custom 안에 GetSung()이라는 메서드 에서 사용 되었습니다. 추상화라는 것은 표현하고자 하는 특성을 간추리는 작업을 의미합니다. Custom 클래스에서 추상적으로 선언된 GetSung() 메서드는 abstract로 선언 되어 있습니다. 그리고 각각의 Man,Woman 클래스에서는 Custom 클래스를 상속 받고 그 안에서 GetString()을 구체화 하여 사용합니다. 각각의 클래스에서 자신의 특성에 맞추어서 구체화 되는 것을 우리는 다형성 이라고 부릅니다. 같은 GetSung()이지만 남자와 여자의 두 개의 특성으로 사용 되는 것입니다. 위의 설계는 추상화의 개념과 다형성의 개념이 잘 표현되고 있습니다.

2.5 다형성과 추상화의 활용

다형성과 추상화를 이용한 예제를 살펴 보았습니다. 이제 객체에 대한 놀라움과 감탄이 나와야 할 타이밍입니다. 하지만 별다른 감동이 오지 않으시는 분들이 대부분일 것입니다. 별 감동이 없으신 분들을 세가지 분류의 사람들로 가려집니다. 첫 번째 경우는 다형성과 추상화의 개념을 자주 도입해 사용 하시고 있는 분이라면 그다지 큰 감동이 오지 않습니다. 괜한 시간낭비를 했다고 생각 할 수 있습니다. _-_; 두 번째는 다형성과 추상화의 개념을 알고는 있었지만 필요성을 느끼지 못해서 사용해오지 않았던 분들입니다. 그리고 마지막으로 객체지향 설계를 한번도 접해 본적이 없으신 분들입니다. 필자의 생각으로는 주로 두 번째 분류의 사람이 대부분이라고 생각이 됩니다. 객체의 개념은 알고 있지만, 별다른 필요성을 느끼지 못해 왔다는 것입니다. 필자 역시 그래 왔었기 때문에 여러분들의 마음을 잘 읽을 수 있습니다. 그래서 특별히 두 번째 사람들의 분류를 겨냥한 돌발 상황을 만들어 보겠습니다.

여러분은 앞서 주문한 프로그램은 완성 하였습니다. 하지만 추상화와 다형성의 개념은 사용하지 않았습니다. 이유는 별 필요성을 느끼지 못해서 라고 해두겠습니다. 프로그램은 정상적으로 아주 잘 작동 되었습니다. 하지만 이 호텔에는 외계인이 자주 잠을 자러 방문합니다. 그런데 외계인은 자신은 중성이라고 자꾸 떼를 씁니다. 외계인 덕분에 요청에 의해서 만들어 준 프로그램은 사용할 수 없었습니다. 호텔 운영자는 중성이라는 요소를 추가시켰으면 좋겠다는 소망이 간절할 뿐입니다.

자, 프로그램 수정! 머 한두 번 있는 일도 아닙니다. 너그럽게 수정을 받아 드리도록 합시다. 자 이제 30%, 90% 의 설계를 살펴 보면서 어떻게 수정해야 될지 같이 생각해 봅시다.

만약 30% 활용한 설계일 경우를 살펴 봅시다. 클래스가 상당히 지저분해 질 것으로 보여집니다. 플러그의 형태를 변화시켜야 되고 GetSung() 이라는 메서드를 수정 해야 합니다. 이것은 깨끗하지 못한 클래스 되어 간다는 것을 볼 수 있습니다. 그럼 객체를 90% 활용한 클래스를 살펴 보도록 합시다. 다형성의 개념을 가지고 있는 이 클래스에서는 단지 중성 클래스를 확장 함으로서 간단히 해결 할 수 있게 됩니다.

객체 지향적 사고는 한번에 자신의 머리 속에 정착 되지 않는 것이 특징입니다. 객체 지향적 사고를 가지기 위해서는 많이 설계해보고 토론해보고 또 생각해보는 노력이 필요합니다.

3. 정리!! 객체지향적 설계 방법

순차적으로 진행 되어야 할 객체 지향 설계 방법을 정리 해보겠습니다.

1) 구현 할 대상(객체)를 선별합니다.

-> 남자, 여자를 구분해 낸 것처럼 연관성 있는 객체들을 분별해 냅니다.

2) 중복 요인을 확인합니다.

-> 중복되는 부분을 뽑아내어 하나의 부모 클래스로 묶는 작업을 말합니다.

3) 상속 받은 후, 각자의 특징대로 클래스를 다시 정의합니다.

-> 고객 클래스를 상속 받은 후 남자, 여자, 중성과 같이 나누는 잡업을 말합니다.

객체 지향에는 정답이 없습니다. "이렇게 설계하는 것이 객체 지향적으로 설계하는 것이다." 것은 극히 주관적인 말이 되는 것입니다, 하지만 효율적인 객체 지향적인 프로그래밍을 하기 위해서는 객체의 특성을 자유롭게 활용 하도록 노력해야만 합니다. 앞의 예제에서 적용해 본 객체의 속성은 추상화(Abstraction), 상속성(Inheritance), 다형성(Polymorphism) 이였습니다. 이 밖에도 캡슐화(Encapsulation), 주체성(Identity), 분류성(Classification)등등의 많은 개념들이 존재 합니다.

작성자 : HOONS(박경훈)
이메일 : tajopkh@hanmail.net
홈페이지 : http://www.hoonsbara.com

Posted by 나비:D
:

BLOG main image
by 나비:D

공지사항

카테고리

분류 전체보기 (278)
Programming? (0)
---------------------------.. (0)
나비의삽질 (5)
Application (177)
C# (28)
JAVA (22)
ASP.NET (3)
C++ (19)
C (18)
.NET (0)
Visual Basic (12)
Flex (16)
Eclipse (8)
Delphi (16)
Visual Studio 2005 (5)
Embed (6)
Linux (2)
Mobile (1)
XML (1)
안드로이드 (2)
SQL (51)
Web (27)
etc. (14)
Omnia (0)
---------------------------.. (0)

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

달력

«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total :
Today : Yesterday :