'language/VB6'에 해당되는 글 2건

간략한 용어 정리

다음은 Visual Basic 개체와 그 기능을 검토하는 과정에서 만나게 되는 용어들입니다. Visual Basic을 사용하기 전에 다른 프로그래밍 언어나 ActiveX(이전의 OLE) 용어로 작업을 했다면 이 부분을 변환하는데 도움이 될 것입니다.

개체에 익숙하지 않으면 모든 것이 다소 당황스러울 것입니다. 개체가 어떻게 이루어졌는지를 보여주는 그림 한 장을 보는 것을 시작으로 부딪치게 될 용어를 간단히 살펴보게 될 것입니다. 이 장의 나머지 부분에서 개체에 대해 좀 더 알게 되면 이 항목에서 제공하는 정보를 전체적으로 이해할 수 있을 것입니다.

시작하기

개체는 캡슐화되어 있습니다. 말하자면 개체는 코드와 데이터를 모두 포함하기 때문에 전통적인 코드 작성 방법보다 쉽게 관리할 수 있습니다.

Visual Basic 개체에는 속성, 메서드, 이벤트가 있습니다. 속성은 개체를 설명하는 데이터입니다. 메서드는 사용자가 원할 때 개체가 실행할 프로시저입니다. 이벤트는 개체가 수행하는 작업으로, 이벤트가 발생했을 때 실행되는 코드를 기록할 수 있습니다.

Visual Basic 개체는 클래스에서 작성되기 때문에 클래스의 인스턴스라고 합니다. 클래스는 개체의 인터페이스, 개체의 공용 여부 그리고 어떠한 상황에서 생성되는지를 정의합니다. 클래스에 대한 설명은 형식 라이브러리에 저장되고 개체 찾아보기로 확인할 수 있습니다.

개체를 사용하려면 개체 변수에 그 참조가 있어야 합니다. 연결 유형은 개체 변수를 사용하여 액세스되는 개체 메서드의 속도를 결정합니다. 개체 변수는 후기 바운드(가장 느림)초기 바운드가 될 수 있습니다. 초기 바운드 변수는 DispID 연결이나 vtable 연결(가장 빠름)이 될 수 있습니다.

속성과 메서드 집합을 인터페이스라고 합니다. Visual Basic 개체의 기본 인터페이스는 세 바인딩 형태를 모두 지원하는 이중 인터페이스입니다. 개체 변수가 강력한 형식 정의(, Dim … As classname)로 되었다면 가장 빠른 형태의 바인딩이 사용됩니다.

기본 인터페이스 외에도 Visual Basic 개체는 다형성을 지원하도록 추가 인터페이스를 구현할 수 있습니다. 다형성으로 각각의 종류에 관계 없이 다양한 종류의 개체를 처리할 수 있습니다. 다중 인터페이스는 구성 요소 개체 모델(Component Object Model COM)의 기능이며, 기존 코드를 유지한 채 새 기능을 추가하여 시간을 낭비하지 않고 프로그램을 발전시킬 수 있습니다.

Visual Basic 클래스는 데이터 인식의 주체가 될 수 있습니다. 클래스는 외부 데이터 원본에 직접 바인딩하여 데이터 소비자가 될 수 있습니다. 또한 다른 개체에 외부 원본의 데이터를 제공하여 데이터 원본이 될 수 있습니다.

끝내기

이 모든 내용에 익숙하다면 이 장의 나머지 내용도 부담없이 살펴 보십시오. 혹시 이 내용에 익숙하지 않다고 해도 걱정할 필요는 없습니다. 이 장의 텍스트 중간 중간에 이 용어들이 가끔 설명되어 있습니다.

 

개체를 포함하는 클래스 찾기

일반 개체 변수(, As Object로 선언한 변수)는 다른 많은 클래스 개체를 포함할 수 있습니다. 마찬가지로 변수는 다른 클래스와 폼을 포함할 수 있는 Visual Basic의 내장된 Form Control 형식으로 선언할 수 있습니다.

이러한 형식의 변수를 사용할 때에는 개체의 클래스에 기초한 다른 동작을 취할 수 있습니다. 예를 들면 어떤 개체는 특정 속성이나 메서드를 지원하지 않을 수 있습니다. Visual Basic은 이것을 해결하기 위해 TypeOf 키워드와 TypeName 함수를 지원합니다.

TypeOf 키워드는 단지 If ... Then ... Else 문 내에서 사용될 수 있습니다. 클래스 이름은 If TypeOf MyControl Is CheckBox Then과 같이 코드에 직접 포함시켜야 합니다.

TypeName 함수는 좀더 복잡합니다. TypeName 함수는 코드 어디에서나 사용할 수 있지만 문자열로 클래스 이름을 반환하기 때문에 문자열 변수로 값을 비교할 수 있습니다.

 

문자열 이름을 사용하여 속성이나 메서드 호출

대부분의 경우 디자인 모드에서 개체의 속성과 메서드를 찾을 수 있으며 이 속성과 메서드를 처리하는 코드를 작성할 수 있습니다. 그러나 어떤 경우에 개체의 속성과 메서드를 미리 알 수 없을 수도 있고 실행 모드에서 사용자가 속성을 지정하거나 메서드를 실행할 수 있도록 지정할 수도 있습니다.

예를 들어 서버 응용 프로그램에 전달하도록 연산자를 입력한 식을 평가하는 클라이언트 응용 프로그램이 있다고 가정합니다. 새 연산자가 필요한 서버에 새로운 함수를 지속적으로 추가하는 경우에는 클라이언트 응용 프로그램을 다시 컴파일하고 다시 배포한 다음 클라이언트 응용 프로그램에서 새 연산자를 사용해야 합니다. CallByName 함수를 사용하여 새 연산자를 문자열로 전달하면 이 클라이언트 응용 프로그램을 변경하지 않아도 됩니다.

CallByName 함수를 사용하면 문자열을 사용하여 실행 모드에서 속성이나 메서드를 지정할 수 있습니다. CallByName 함수의 서명은 다음과 같습니다.

Result = CallByName(Object, ProcedureName, CallType, Arguments())

CallByName의 첫째 인수는 실행할 개체의 이름입니다. 둘째 인수 ProcedureName은 실행할 메서드나 속성 프로시저입니다. CallType 인수는 method(vbMethod), property let(vbLet), property get(vbGet), property set(vbSet)과 같이 실행할 프로시저 형식을 나타내는 상수입니다. 마지막 인수는 선택적 인수이며 프로시저에 대한 모든 인수를 갖는 variant 배열입니다.

SquarRoot 함수가 있는 MathServer 서버 응용 프로그램이 있다고 가정합니다. 응용 프로그램에 두 개의 TextBox 컨트롤이 있습니다. Text1 컨트롤에는 계산할 식이 있으며 Text2 컨트롤을 함수의 이름을 입력하는 데 사용할 경우 아래와 같은 코드를 CommandButton Click 이벤트에 사용하여 Text1의 식에 대한 SquareRoot 함수를 발생시킬 수 있습니다.

Private Sub Command1_Click()
   Text1.Text = CallByName(MathServer, Text2.Text, vbMethod, Text1.Text)
End Sub

Text1 "64 / 4"를 입력하고 Text2 "SquareRoot"를 입력하면 위의 코드는 계산될 식을 포함하는 문자열을 필요한 인수로 갖는 SquareRoot 함수를 발생시킨 후 Text1 16이나 64 4로 나눈 값의 제곱근인 "4"를 반환합니다. Text2에 잘못된 문자열을 입력하거나, 메서드 대신 속성 이름을 문자열로 사용하는 경우 메서드가 추가 인수를 필요로 하는 경우에는 런타임 오류가 발생합니다. CallByName을 사용하여 이러한 오류나 다른 오류가 발생할 것이 예상되면 강력한 오류 처리 코드를 추가해 두는 것이 좋습니다.

경우에 따라 CallByName 함수를 사용하는 것이 좋을 수도 있지만 성능에 대한 유용성을 잘 생각해 보아야 합니다. CallByName을 사용하여 프로시저를 실행하면 후기 바운드 호출보다 약간 느리게 됩니다. 루프에서처럼 반복적으로 호출해야 하는 함수를 실행할 경우에 CallByName 함수를 사용하면 성능에 상당한 영향을 끼칠 수 있습니다.

 

개체에서 여러 작업 수행

한 개체에서 여러 작업을 수행해야 하는 경우가 있습니다. 예를 들어 한 개체에 여러 속성을 설정해야 하는 경우입니다. 이와 같은 경우 필요한 수만큼 문을 사용합니다.

Private Sub Form_Load()
   Command1.Caption = "확인"
   Command1.Visible = True
   Command1.Top = 200
   Command1.Left = 5000
   Command1.Enabled = True
End Sub

모든 문에서 같은 개체 변수 Command1을 사용한다는 것에 주목하기 바랍니다. With...End With 문을 사용하면 이 코드를 읽고 쓰기 쉬울 뿐만 아니라 더욱 효과적으로 실행할 수 있습니다.

Private Sub Form_Load()
   With Command1
      .Caption = "확인"
      .Visible = True
      .Top = 200
      .Left = 5000
      .Enabled = True
   End With
End Sub

With...End With 문을 With...End With 내에 포함시켜 중첩된 With 문을 만들 수도 있습니다.

 

기본 속성 사용

많은 개체에는 기본 속성이 있습니다. 값을 설정할 경우에는 속성을 명확하게 참조하지 않아도 되기 때문에 기본 속성을 사용하여 코드를 단순화할 수 있습니다. Value 속성이 기본 속성인 개체의 경우 이 두 문은 같은 기능을 합니다.

object = 20

그리고

object.Value = 20

기본 속성이 어떻게 사용되는지를 보려면 폼에 CommandButton TextBox를 그린 다음 CommandButton Click 이벤트에 다음 문을 추가합니다.

Text1 = "안녕하십니까?"

응용 프로그램을 실행하고 CommandButton을 누릅니다. Text TextBox의 기본 속성이기 때문에 TextBox "안녕하십니까?"가 표시됩니다.

개체 변수와 함께 기본 속성 사용

개체 참조가 개체 변수에 저장되면 기본 속성을 계속 사용할 수 있으며 다음 코드에서 확인할 수 있습니다.

Private Sub Command1_Click()
   Dim obj As Object
   ' 개체 변수에 Text1에 대한 참조를 저장합니다.
   '   variable.
   Set obj = Text1
   ' 기본 속성값(Text)을 설정합니다.
   obj = "안녕하십니까?"
End Sub

위의 코드에서 obj = "안녕하십니까?"obj.Text = "안녕하십니까?"라고 입력하는 것과 같습니다.

Variant와 함께 기본 속성 사용

개체 변수에 저장하지 않고 Variant 형식의 변수에 개체 참조를 저장하면 기본 속성을 액세스하는 것이 달라집니다.

예를 들어 Variant 형식의 참조를 사용하여 Text1의 기본 속성을 읽을 수 있지만 기본 속성에 "goodbye" 문자열을 할당할 수 없습니다. 대신 Variant 형식은 문자열에 대한 참조로 바뀝니다.

이것이 어떻게 수행되는지 보려면 앞의 예제에서 CommandButton Click 이벤트에 다음 코드를 입력하십시오.

Private Sub Command1_Click()
   Dim vnt As Variant
   ' "안녕하십니까?"에 기본 속성(텍스트)을 설정합니다.
   Text1 = "안녕하십니까?"
   ' Variant Text1에 대한 참조 위치를 지정합니다.
   Set vnt = Text1
   ' Text1의 기본 속성을 표시하고,
   ' Variant에 개체 참조가 있는 것을 나타냅니다.
   MsgBox vnt, , "IsObject? " & IsObject(vnt)
   ' Text1의 기본 속성을 설정합니다.
   vnt = "goodbye"
   MsgBox vnt, , "IsObject? " & IsObject(vnt)
End Sub

응용 프로그램을 실행하고 CommandButton을 누르면 우선 Text1의 현재 기본 속성값, "안녕하십니까?"를 표시하는 메시지 상자가 나타납니다. 이는 Text1을 봄으로써 검증할 수 있습니다. 메시지 상자 캡션은 Variant에 개체 참조(, Text1에 대한 참조)가 있는지 확인해 줍니다.

메시지 상자에서 확인 단추를 누르면 Text1에 대한 참조를 없애고 "goodbye" Variant에 할당됩니다. 그러면 Variant의 내용을 나타내는 다른 메시지 상자가 나타나며, 보이는 것과 같이 현재 Text1.Text 값과 일치하지 않습니다.

메시지 상자 캡션에서 Variant에 더 이상 개체 참조가 없고 "goodbye" 문자열이 있는 것을 확인할 수 있습니다.

추가 정보   Variant와 기타 데이터 형식에 대한 자세한 내용은 "프로그래밍 기초" "변수, 상수, 데이터 형식 개요"를 참조하십시오.

Variants로 개체를 사용하는 다른 방법은 "Visual Basic Collection 개체"를 참조하십시오.

 

개체 배열 작성

다른 데이터 형식의 배열을 선언하고 사용하는 것처럼 개체 유형의 배열을 선언하고 사용할 수 있습니다. 이 배열은 고정 배열이거나 동적 배열일 수 있습니다.

폼 변수의 배열

다른 데이터 형식의 배열을 선언하는 것과 같은 방법으로 Private, Dim, ReDim, Static, Public 등과 함께 폼 배열을 선언할 수 있습니다. New 키워드와 함께 배열을 선언할 경우 배열에서 요소를 사용하는 것처럼 Visual Basic에서 자동으로 배열의 각 요소에 새 폼 인스턴스를 작성합니다.

Private Sub Command1_Click ()
   Dim intX As Integer
   Dim frmNew(1 To 5) As New Form1
   For intX = 1 To 5
      frmNew(intX).Show
      frmNew(intX).WindowState = vbMinimized
      ' 폼이 정상적 크기로 나타나지 않고
      ' 바로 최소화되기를 원한다면
      ' 위의 두 줄 순서를 반대로 합니다.
   Next
End Sub

위에 있는 코드를 실행하기 위해 CommandButton을 누르면 최소화된 다섯 개의 Form1 인스턴스가 작성됩니다.

메모   작업 표시줄에 Form1여섯번 나타납니다. 코드를 시작한 Form1 인스턴스는 최소화되지 않습니다.

컨트롤 변수의 배열

다른 데이터 형식의 배열을 선언하는 것과 같은 방법으로 Private, Dim, ReDim, Static, Public 등과 함께 컨트롤의 배열을 선언할 수 있습니다. 그러나 폼 배열과는 달리 New 키워드와 함께 컨트롤 배열을 선언할 수 없습니다. 예를 들어 특정 컨트롤 유형이 되도록 배열을 선언할 수 있습니다.

ReDim ActiveImages(10) As Image

특정 컨트롤 유형이 되도록 배열을 선언하면 해당 유형의 컨트롤만 배열에 할당할 수 있습니다. 앞의 선언을 예를 들면, 이미지 컨트롤만 배열에 할당할 수 있습니다. 하지만 이 이미지 컨트롤을 다른 폼에서 가져올 수는 있습니다.

이것과 달리 다양한 컨트롤 유형을 포함할 수 있는 Controls 컬렉션 배열의 참조는 모두 같은 폼에 있어야 합니다.

다른 방법으로는 포괄적인 컨트롤 변수의 배열을 선언할 수 있습니다. 예를 들어 특정 컨트롤에 끌어놓은 모든 컨트롤을 검사하여 각 컨트롤을 해당 컨트롤에 한번만 끌어 놓으려면 끌어서 놓은 각 컨트롤에 대한 참조가 있는 컨트롤 변수의 동적 배열을 유지함으로써 이것을 가능하게 할 수 있습니다.

Private Sub List1_DragDrop(Source As VB.Control, _
      X As Single, Y As Single)
   Dim intX As Integer
   Static intSize As Integer
   Static ctlDropped() As Control
   For intX = 1 To intSize
      ' 끌어 놓은 컨트롤이 배열에 있으면
      ' 이미 여기에 한 번 끌어 놓은 것입니다.
      If ctlDropped(intX) Is Source Then
         Beep
         Exit Sub
      End If
   Next
   ' 배열을 확대합니다.
   intSize = intSize + 1
   ReDim Preserve ctlDropped(intSize)
   ' 끌어 놓은 컨트롤에 대한 참조를 저장합니다.
   Set ctlDropped(intSize) = Source
   ' ListBox에 컨트롤 이름을 추가합니다.
   List1.AddItem Source.Name
End Sub

예제는 Is 연산자로 컨트롤 인수와 컨트롤 배열의 변수를 비교하는 것을 보여줍니다. Is 연산자는 Visual Basic 개체 참조를 식별하는 검사에 사용할 수 있습니다. 비교한 참조 두 개가 같은 개체에 대한 것이라면 Is 연산자는 True를 반환합니다.

예제에서 Source 인수의 개체 참조를 배열 요소에 할당하는데 Set 문을 사용한 것에 주의하십시오.

추가 정보   언어 참조 "Is 연산자"를 참조하십시오.

배열에 대한 자세한 내용은 "배열", "동적 배열"을 참조하십시오.

개체를 쉽게 관리하는 방법에 대한 내용은 이 장 뒷부분의 "개체 컬렉션 작성"을 참조하십시오.

 

개체 컬렉션 작성

컬렉션은 유용한 개체 관리 방법을 제공합니다. 배열과 달리 Collection 개체는 구성원을 추가하고 삭제할 때 다시 선언하지 않아도 됩니다.

예를 들어 특정 컨트롤에 끌어 놓은 각 컨트롤에 대한 참조가 있는 컨트롤 변수의 동적 배열을 유지함으로써 특정 컨트롤에 끌어 놓은 모든 컨트롤을 추적할 수 있지만 컨트롤을 한 번만 놓을 수 있습니다.

Private Sub List1_DragDrop(Source As VB.Control, _
X As Single, Y As Single)
   Dim vnt As Variant
   Static colDroppedControls As New Collection
   For Each vnt In colDroppedControls
      ' 끌어놓은 컨트롤이 컬렉션에 있으면
      ' 이미 여기에 한 번 끌어 놓은 것입니다.
      If vnt Is Source Then
         Beep
         Exit Sub
      End If
   Next
   ' 끌어 놓은 컨트롤에 대한 참조를 저장합니다.
   colDroppedControls.Add Source
   ' ListBox에 컨트롤 이름을 추가합니다.
   List1.AddItem Source.Name
End Sub

예제는 Is 연산자로 colDroppedControls 컬렉션의 개체 참조와 끌어 놓은 컨트롤의 참조를 가지는 이벤트 인수를 비교하는 것을 보여줍니다. Is 연산자는 Visual Basic 개체 참조를 식별하는 검사에 사용할 수 있습니다. 같은 개체의 다른 두 참조를 비교하면 Is 연산자는 True를 반환합니다.

예제에서 컬렉션에 끌어 놓은 컨트롤 참조 위치를 지정하는데 Collection 개체의 Add 메서드도 사용합니다.

배열과 달리 Collection은 개체 자체입니다. Collection 클래스의 인스턴스는 코드에서 처음 변수를 참조할 때 작성되기 때문에 colDroppedControls 변수는 As New로 선언됩니다. 이벤트 프로시저가 끝났을 때 Collection 개체가 파괴되지 않도록 Static 변수로 선언합니다.

추가 정보   언어 참조 "Is 연산자"를 참조하십시오.

Collection 개체의 속성과 메서드는 이 장 뒷부분의 "Visual Basic Collection 개체"에서 자세히 설명합니다.

위의 코드와 배열 사용에 필요한 코드를 비교하려면 이 장 앞부분의 "개체 배열 작성"을 참조하십시오.

Collection 개체를 사용자 컬렉션 클래스로 확장하여 더욱 생산적인 컬렉션을 작성하는 방법에 대한 자세한 내용은 이 장 뒷부분의 "사용자 고유 컬렉션 클래스 작성"을 참조하십시오.

이 장 앞부분의 "Visual Basic 개체에 대하여 알아야 할 사항"에서는 개체가 작성되고 소멸되는 과정을 설명합니다.

 

Visual Basic Collection 개체

컬렉션은 관련 항목 집합을 그룹화하는 방법입니다. 컬렉션은 프로그램에서 로드한 폼(Forms 컬렉션)이나 폼의 모든 컨트롤(Controls 컬렉션) 등 많은 것을 관리하기 위해 Visual Basic에서 사용합니다.

Visual Basic에서는 사용자 컬렉션을 정의할 수 있도록 포괄적인 Collection 클래스를 제공합니다. 필요한 만큼 Collection 개체(Collection 클래스의 인스턴스)를 작성할 수 있습니다. 이 장 뒷부분의 "사용자 고유 컬렉션 클래스 작성" "개체 모델"의 설명처럼 Collection 개체를 사용자 컬렉션 클래스와 개체 모델의 기초로 사용할 수 있습니다.

예를 들어 컬렉션은 다중 폼을 관리하는 좋은 방법입니다. "사용자 인터페이스 작성" "MDI 응용 프로그램"에서는 문서 창을 얼마든지 열 수 있는 응용 프로그램에 대해 설명합니다. 다음 코드는 컬렉션 개체의 Add 메서드를 사용하여 사용자가 작성한 MDI 하위 창 목록을 유지하는 방법을 나타냅니다. 이 코드는 MDIChild 속성이 True로 설정된 mdiDocument라는 이름을 가진 폼이 있다고 가정합니다.

' 상위 MDIForm의 모듈 수준 컬렉션
Public colDocuments As New Collection
 
'  MDI 하위 문서 폼 작성 코드
Private Sub mnuFileNew()
   Dim f As New mdiDocument
   Static intDocumentNumber As Integer
   intDocumentNumber = intDocumentNumber + 1
   ' 다음 줄은 폼을 작성합니다.
   f.Caption = "문서" & intDocumentNumber
   ' 컬렉션에 개체 참조를 추가합니다.
   colDocuments.Add f
   f.Show
End Sub

colDocuments 컬렉션은 mdiDocument 폼의 인스턴스만 포함하면서 제공된 Forms 컬렉션의 부분 집합처럼 행동합니다. 컬렉션 크기는 각각의 새 폼이 추가되면 자동으로 조정됩니다. For Each ... Next를 사용하여 컬렉션의 내용을 추적할 수 있습니다. 검색할 수 있는 키를 폼에 지정하고자 하는 경우 이 단락 뒷부분의 설명처럼 텍스트 문자열을 Add 메서드의 둘째 매개 변수로 적용할 수 있습니다.

colDocuments 변수 선언에서 New 키워드를 사용하면 코드에서 변수를 처음 참조할 때 Collection 개체가 작성됩니다. Collection은 데이터 형식이 아닌 클래스이기 때문에 인스턴스를 작성하고 변수에서 해당 인스턴스(개체)에 대한 참조를 유지해야 합니다.

다른 개체처럼 Collection 개체는 참조가 있는 마지막 변수가 Nothing으로 설정되거나 범위를 벗어나면 소멸됩니다. 그리고 포함하는 모든 개체 참조를 해제합니다. 그 때문에 프로그램 사용 기간 동안 존재하도록 colDocuments 변수가 상위 MDIForm에 선언됩니다.

메모   컬렉션을 폼 추적에 사용할 경우 폼을 언로드한 후에 컬렉션의 Remove 메서드를 사용하여 컬렉션의 개체 참조를 삭제합니다. 폼에 대한 참조가 존재하고 Collection 개체가 유지하고 있는 참조가 개체 변수에 있는 참조만큼 유효한 동안은 폼에서 사용 중인 메모리를 사용할 수 없습니다.

Collection 개체의 구성 요소

Collection 개체는 Variant 형식에 각 항목을 저장합니다. 따라서 Collection 개체에 추가할 수 있는 목록은 Variant에 저장할 수 있는 목록과 같습니다. 여기에는 표준 데이터 형식, 개체, 배열 등이 포함되지만 사용자 정의 형식은 포함되지 않습니다.

Variants는 저장된 것에 관계 없이 항상 16바이트를 사용하므로 Collection 개체보다 배열을 사용하는 것이 더 효율적인 것이 사실입니다. 그러나 Collection 개체를 ReDim하지 않는 것이 결과적으로 더욱 완전하고 유지하기 좋은 코드가 됩니다. 게다가 Collection 개체에는 배열에 없는 매우 빠른 키 검색 도구가 있습니다.

메모   데이터가 실제로 다른 위치에 저장되어 있을 경우에도 Variant는 항상 정확히 16바이트를 사용합니다. 예를 들어 문자열이나 배열을 Variant에 할당하면 Variant에 문자열이나 배열 데이터 사본의 포인터가 포함됩니다. 32비트 시스템에서 Variant 4바이트만 포인터에 사용되고 Variant 내부에는 실제로 데이터가 없습니다.

개체를 저장하면 개체 변수에서처럼 Variant에 개체 참조가 포함됩니다. 문자열과 배열에서처럼 Variant 4바이트만 사용됩니다.

숫자 데이터 형식은 Variant 안에 저장됩니다. 데이터 형식에 관계 없이 Variant는 계속 16바이트만 사용합니다.

Variants의 크기에도 불구하고 Collection 개체로 위에서 이야기한 모든 데이터 형식을 저장하기 위해 사용됩니다. Collection 개체로 Variants에 항목을 저장하는 대신 더욱 완전하고 관리하기 좋은 코드를 작성할 수 있습니다.

Collection 개체의 속성과 메서드

Collection 개체는 컬렉션에서 항목을 삽입, 삭제, 검색하는데 사용할 수 있는 속성과 메서드를 가집니다.

속성이나 메서드

설명

Add 메서드

컬렉션에 항목을 추가합니다.

Count 속성

컬렉션 항목의 갯수를 반환합니다.

읽기 전용

Item 메서드

인덱스나 키별로 항목을 반환합니다.

Remove 메서드

인덱스나 키별로 컬렉션에서 항목을 삭제합니다.


 

이 속성과 메서드는 컬렉션의 가장 기본적인 기능을 제공합니다. 예를 들어 Add 메서드는 컬렉션에 추가될 개체 유형을 확인할 수 없으므로 컬렉션에 한 종류의 개체만 있는지 확인할 수 없습니다. 이 장 뒷부분의 "사용자 고유 컬렉션 클래스 작성"의 설명처럼 사용자 컬렉션 클래스를 작성하여 더욱 생산적인 기능과 추가 속성, 메서드, 이벤트 등을 제공할 수 있습니다.

컬렉션에서 추가, 삭제, 검색하는 기본 기능은 키와 인덱스에 의존합니다. String 값으로 이름, 운전 면허 등록 번호, 주민 등록 번호 또는 단순히 String으로 변환된 정수 등이 될 수 있습니다. 이 단락 뒷부분에서 설명한 것처럼 Add 메서드로 키를 항목과 연결할 수 있습니다.

인덱스는 범위가 1과 컬렉션의 항목 수 사이인 Long 데이터입니다. beforeafter로 명명된 인수를 사용하여 항목의 인덱스 초기값을 제어할 수 있지만 다른 항목이 추가되거나 삭제될 때 값이 변경됩니다.

메모   "Visual Basic 컬렉션"의 설명처럼 인덱스가 1로 시작하는 컬렉션을 "1 기준"이라고 합니다.

인덱스를 사용하여 컬렉션의 항목을 제어할 수 있습니다. 예를 들어 다음 코드는 mcolEmployees 변수에 Collection 개체 참조가 있다는 가정하에 Employee 개체 컬렉션에 저장한 모든 직원에게 10%의 인상을 적용하는 두 가지 방법을 나타냅니다.

Dim lngCt As Long
For lngCt = 1 To mcolEmployees.Count
   mcolEmployees(lngCt).Rate = _
      mcolEmployees(lngCt).Rate * 1.1
Next
 
Dim emp As Employee
For Each emp In mcolEmployees
   emp.Rate = emp.Rate * 1.1
Next

   성능을 향상시키려면 For Each를 사용하여 Collection 개체의 항목을 제어하십시오. For Each는 인덱스로 제어하는 것보다 훨씬 빠릅니다. 이것은 모든 컬렉션 구현에 적용되는 것이 아니며 컬렉션에서 데이터를 내부적으로 저장하는 방법에 따라 다릅니다.

컬렉션에 항목 추가

Add 메서드로 컬렉션에 항목을 추가합니다. 구문은 다음과 같습니다.

Sub Add (item As Variant [, key As Variant] [, before As Variant]

[, after As Variant] )

예를 들어 작업 주문 ID 속성을 키로 사용하여 작업 주문 컬렉션에 작업 주문 개체를 추가하려면 다음과 같이 기록합니다.

colWorkOrders.Add woNew, woNew.ID

위의 예에서 ID 속성은 문자열로 간주합니다. 속성이 숫자(예를 들어 Long)일 경우 CStr 함수를 사용하여 키에 필요한 문자열 값으로 변환합니다.

colWorkOrders.Add woNew, CStr(woNew.ID)

Add 메서드는 명명된 인수를 지원합니다. 현재 추가하려는 항목을 셋째 요소로 추가하려면 다음과 같이 기록합니다.

colWorkOrders.Add woNew, woNew.ID, after:=2

beforeafter 이름의 인수를 사용하여 개체 컬렉션의 순서를 관리할 수 있습니다. 예를 들어 Collection 개체는 1부터 시작하기 때문에 before:=1은 컬렉션 시작 부분에 항목을 삽입합니다.

컬렉션에서 항목 삭제

Remove 메서드를 사용하여 컬렉션에서 항목을 삭제합니다. 구문은 다음과 같습니다.

object.Remove index

index 인수는 삭제할 항목 위치나 항목 키가 될 수 있습니다. 컬렉션에서 제3의 요소 키가 "W017493"일 경우 다음 두 문장 중 하나를 사용하여 삭제할 수 있습니다.

colWorkOrders.Remove 3

-또는-

colWorkOrders.Remove "W017493"

컬렉션에서 항목 검색

Item 메서드를 사용하여 컬렉션에서 특정 항목을 검색합니다. 구문은 다음과 같습니다.

[Set] variable = object.Item(index)

Remove 메서드에서처럼 인덱스는 컬렉션 위치나 항목 키가 될 수 있습니다. Remove 메서드와 같은 예제를 사용하여 다음 두 문장 중 하나로 컬렉션에서 셋째 요소를 추출할 수 있습니다.

Set woCurrent = colWorkOrders.Item(3)

-또는-

Set woCurrent = colWorkOrders.Item("W017493")

정수를 키로 사용할 경우 CStr 함수를 사용하여 문자열로 변환한 다음 Item이나 Remove 메서드로 보내야 합니다. Collection 개체는 항상 정수를 인덱스로 간주합니다.

메모   전달할 값이 인덱스인지 키인지를 Collection 개체가 결정하지 않도록 하십시오. 키로 사용하려는 변수가 String이 아닐 경우 CStr을 사용하여 변환합니다. 인덱스로 사용하고자 하는 값이 정수 데이터 형식이 아닐 경우 CLng를 사용하여 변환합니다.

기본 메서드: Item 메서드

Item 메서드는 Collection 개체의 기본 메서드이므로 컬렉션의 개체에 액세스할 때 생략할 수 있습니다. 따라서 이전 코드 예제를 다음과 같이 기록할 수도 있습니다.

Set woCurrent = colWorkOrders(3) 

-또는-

Set woCurrent = colWorkOrders("W017493")

중요   Collection 개체는 요소를 추가하거나 삭제할 경우 자동으로 인덱스 수를 관리합니다. 따라서 주어진 요소의 인덱스는 그때 그때 변경됩니다. 인덱스 값을 저장한 후 나중에 프로그램에서 같은 요소를 검색할 수 없을 수도 있습니다. 이후에 같은 요소를 제어하기 위한 목적이라면 키 값을 사용해야 합니다.

Item 메서드를 사용하여 속성과 메서드 실행

개체 참조를 사용하기 위해 컬렉션에서 검색하거나 개체 변수에 저장할 필요는 없습니다. 컬렉션에 있는 동안 참조를 사용할 수 있습니다.

예를 들어 위의 코드에서 WorkOrder 개체에 Priority 속성이 있다고 가정할 경우 다음 두 문장은 모두 작업 주문의 우선 순위를 설정합니다.

colWorkOrders.Item("W017493").Priority = 3
colWorkOrders("W017493").Priority = 3

Visual Basic은 왼쪽에서 오른쪽으로 식을 계산하기 때문에 이와 같은 작업을 할 수 있습니다. Item 메서드(명시적이든 아니든)가 사용되면 Visual Basic에서는 지시된 항목(이 경우에는 키가 W017493WorkOrder 개체)에 대한 참조를 받아 이 참조로 나머지 코드 줄을 실행합니다.

   컬렉션에서 개체의 속성이나 메서드를 여러 번 불러오려면 우선 명확히 형식 선언된 개체 변수에 개체 참조를 복사합니다. Collection 개체는 Variants에 항목을 저장하기 때문에 컬렉션에 있는 동안 개체 참조를 사용하는 것보다 입력된 개체 변수(: Dim woCurrent As WorkOrder)에 복사한 후에 사용하는 것이 더 빠릅니다. Variants의 개체 참조는 항상 가장 느리게 연결됩니다.

추가 정보   Collection 개체는 배열을 대신하여 다양한 일상적인 프로그래밍 작업에서 유용하게 사용할 수 있습니다. "프로그래밍 추가 정보"에서 "배열 대신 컬렉션 사용"을 참조하십시오.

Visual Basic에서는 다양한 기본 제공 컬렉션을 제공합니다. Collection 개체와 비교하려면 "Visual Basic 컬렉션"을 참조하십시오.

 

Visual Basic 컬렉션

컬렉션이란 "Visual Basic Collection 개체"에서 관련 개체를 그룹화하는 방법으로 정의되어 있습니다. 그러나 다양한 해석이 가능하며, 이 정의 외의 추가 개념이 있습니다.

사실 컬렉션을 비교할 때 보게 되는 것처럼 Visual Basic에서 제공하는 컬렉션 종류 사이에도 많은 차이점이 있습니다. 예를 들어 다음 코드에서는 오류가 발생합니다.

Dim col As Collection
Set col = Forms      ' 오류가 발생하였습니다!

왜 오류가 발생했을까요? Forms 컬렉션은 컬렉션 개체입니다. col 변수는 As Collection으로 선언된 변수입니다. Forms에 대한 참조를 col 변수에 할당할 수 없을까요?

그 이유는 Collection 클래스와 Forms 컬렉션은 다형적이지 않기 때문입니다. , 개별 코드에 기초하여 개발되었기 때문에 서로 교환할 수 없습니다. 그 둘은 서로 메서드가 다르고, 개체 참조 방식이나 인덱스 값을 다르게 가지게 됩니다.

이것은 실제로 컬렉션 구현 가능성 중의 하나를 나타내기 때문에 Collection 클래스 이름이 잘못 사용된 경우가 됩니다. 이 항목에서는 생길 수 있는 몇 가지 구현 차이점을 알아봅니다.

0부터 시작하는 컬렉션과 1부터 시작하는 컬렉션

컬렉션은 인덱스 종류에 따라 0부터 시작하거나 1부터 시작하는 컬렉션이 됩니다. 짐작대로 컬렉션의 첫째 항목 인덱스가 0부터 시작하는 컬렉션은 영(0)이고 1부터 시작하는 컬렉션은 1입니다. 0부터 시작하는 컬렉션의 예로는 Forms Controls 컬렉션이 있습니다. Collection 개체는 1부터 시작하는 컬렉션입니다.

Visual Basic에 처음부터 있었던 컬렉션은 0부터 시작하는 컬렉션과 비슷한 반면 최근에 추가된 컬렉션은 1부터 시작하는 컬렉션에 더 가깝습니다. 1부터 시작하는 컬렉션은 인덱스 범위가 1부터 Count(컬렉션에서 항목 수를 반환하는 Count 속성)까지 이므로 더욱 직관적으로 사용할 수 있습니다.

이와 대조적으로 0부터 시작하는 컬렉션은 0부터 Count 속성보다 1이 작은 범위를 가집니다.

인덱스와 키 값

다양한 Visual Basic 컬렉션은 Visual Basic Collection 개체처럼 숫자 인덱스나 문자열 키를 사용하여 항목을 액세스할 수 있습니다. 그러나 Visual Basic Collection 개체는 키를 지정하지 않고 항목을 추가할 수 있습니다.

이와는 대조적으로 Forms 컬렉션은 폼과 연관된 고유 문자열 값이 없기 때문에 숫자 인덱스만 허용합니다. 예를 들어 캡션이 같은 다중 폼이나 Name 속성이 같은 폼을 여러 개 로드할 수 있기 때문입니다.

항목 추가와 삭제

또한 컬렉션은 항목 추가 여부와 항목 추가 방법에 따라 다릅니다. 예를 들어 Visual Basic 코드를 사용하여 Printers 컬렉션에 프린터를 추가할 수 없습니다.

Collection 개체는 일반적인 프로그래밍 도구이기 때문에 다른 컬렉션보다 더 유동적입니다. 컬렉션에 항목을 추가하는데 사용할 수 있는 Add 메서드와 항목을 삭제하는 Remove 메서드가 있습니다.

이와 대조적으로 폼을 Forms 컬렉션에 추가하는 유일한 방법은 폼을 로드하는 것입니다. New 연산자를 사용하여 폼을 작성하거나 As New로 선언된 변수를 참조하여 폼을 작성할 경우 Load 문을 사용하여 로드해야 Forms 컬렉션에 추가할 수 있습니다.

Forms Controls 컬렉션에는 Remove 메서드가 없습니다. Load Unload 문을 사용하여 직접 이 컬렉션에서 폼과 컨트롤을 추가하고 삭제합니다.

내부를 더 자세히 살펴보면

위에서 설명한 것처럼 폼을 로드해야 Forms 컬렉션에 추가할 수 있습니다. 따라서 가장 정확한 Forms 컬렉션의 사양은 프로그램에 현재 로드해온 모든 폼이 있는 것입니다.

그러나 완전히 정확하지는 않습니다. 프로젝트에서 Microsoft Office와 호환되도록 포함된 Microsoft Forms을 사용할 경우 UserForms이라는 개별 컬렉션에서 이 폼을 찾을 수 있습니다. 따라서 Forms 컬렉션에는 프로그램에 현재 불러온 모든 Visual Basic 이 있습니다.

Collection 클래스 내용은 매우 정확하게 지정됩니다. Variant에 저장할 수 있는 것이라면 어떤 것이라도 가능합니다. 따라서 Collection 개체는 개체나 정수를 포함할 수 있지만 사용자 정의 형식은 포함할 수 없습니다.

유감스럽게도 이 사양은 많은 범위를 차지하므로 주어진 Collection 클래스의 인스턴스는 데이터 형식, 배열, 개체 등 잡다한 것을 저장할 수 있습니다.

   "사용자 고유 컬렉션 클래스 작성"에서 설명한 것처럼 사용자 고유 컬렉션 클래스를 작성하는 가장 중요한 이유는 컬렉션의 내용을 제어할 수 있는 것으로 형식 안전이라는 개념입니다.

컬렉션 열거

For Each … Next를 사용하면 컬렉션이 0부터 시작하는지 1부터 시작하는지에 관계 없이 컬렉션 내의 항목을 열거할 수 있습니다. 물론 Visual Basic에서 For Each … Next를 사용하여 배열에 항목을 열거할 수 있기 때문에 이것이 컬렉션의 특징이라고 할 수는 없습니다.

For Each … Next를 작동시키는 것은 열거자(enumerator)라는 작은 개체입니다. 열거자는 컬렉션에서 있는 장소를 추적하고 필요할 때 다음 항목을 반환합니다.

배열을 열거하려면 Visual Basic은 실행 시간에 열거자 개체 배열을 작성합니다. 컬렉션에는 고유의 열거자 개체가 있으며 필요시 작성됩니다.

열거자는 항목을 건너뛰지 않습니다

Visual Basic 컬렉션의 열거자는 항목을 건너뛰지 않습니다. 예를 들어 "A", "B", "C"가 있는 컬렉션을 열거하는 중간에 "B"를 삭제할 경우 Visual Basic 컬렉션은 "C"로 건너뛰지 않습니다.

열거자는 추가된 항목을 확인하지 못할 수 있습니다

열거하는 동안 항목을 컬렉션에 추가할 경우 일부 열거자는 추가된 항목을 포함시키고 일부는 포함시키지 않습니다. 예를 들어 Forms 컬렉션은 열거하는 동안 로드해온 폼을 열거하지 못합니다.

컬렉션 개체의 경우는 끝부분에 항목 추가를 허용할 경우 Collection 개체는 열거하는 동안 추가한 항목을 열거합니다. 따라서 다음 고리는 끝나지 않고 계속되며 <Ctrl+Break>를 눌러야 끝납니다.

Dim col As New Collection
Dim vnt As Variant
col.Add "Endless"
col.Add "Endless"
For Each vnt In col
   MsgBox vnt
   col.Add "Endless"
Next

반면에 컬렉션 시작 부분에 추가하는 항목은 열거에 포함되지 않습니다.

Dim col As New Collection
Dim vnt As Variant
col.Add "Will be enumerated"
For Each vnt In col
   MsgBox vnt
   ' 시작 부분에 항목을 추가합니다.
   col.Add "Won't be enumerated", Before:=1
Next

열거자의 정의

For Each … Next가 시작할 때마다 새 열거자를 생성함으로써 컬렉션은 열거가 중첩되는 것을 허용합니다. 예를 들어 mcolStrings 변수에 Collection 개체에 대한 참조가 있고 컬렉션에는 문자열만 있을 경우 다음 코드는 다른 두 문자열 조합 모두를 인쇄합니다.

Dim vnt1 As Variant
Dim vnt2 As Variant
For Each vnt1 In mcolStrings
   For Each vnt2 In mcolStrings
      If vnt1 <> vnt2 Then
         Debug.Print vnt1 & " " & vnt2
      End If
   Next
Next

추가 정보   이 장 뒷부분의 "사용자 고유 컬렉션 클래스 작성"을 참조하십시오.

 

사용자 고유 컬렉션 클래스 작성

컬렉션을 사용한 개체 포함을 구현할 수 있는 일반 접근 방법이 세 가지 있습니다. "개체 모델"에서 설명한 SmallBusiness 개체의 Employees 컬렉션에서 이 컬렉션을 구현하려면 다음을 수행해야 합니다.

  • SmallBusiness 클래스 모듈에서 Employees 변수를 As Collection으로 선언하고 Public으로 설정합니다. 이것이 쉬운 해결책입니다.

     
  • SmallBusiness 클래스 모듈에서 mcolEmployees 변수를 As Collection으로 선언하고 Private으로 설정합니다. SmallBusiness 개체에 개체 추가와 삭제를 위한 일련의 메서드를 제공합니다. 이것은 세 가지 방법 중 적어도 개체 지향적이기는 합니다.

     
  • 이 장 뒷부분에서 설명할 Employees라는 컬렉션 클래스 모듈을 작성하여 사용자 컬렉션 클래스를 구현합니다. SmallBusiness 개체에 Employees 클래스의 읽기 전용 속성을 설정합니다.

전략은 견고함 증가 순서대로 나열되어 있습니다. 초가집, 나무집, 벽돌집 등의 접근법으로 특성을 나타낼 수 있습니다.

  • 공용 컬렉션 예제: 초가집   Collection 개체의 지나친 유연성은 KitchenSink 개체를 포함한 모든 것을 Collection에 추가할 수 있음을 드러냅니다.

     
  • 전용 컬렉션 예제: 나무집   Collection 개체를 전용으로 설정하여 약간의 견고함을 얻을 수 있지만 컬렉션에서 For Each Next를 사용할 수 없게 됩니다.

     
  • 사용자 컬렉션 클래스 작성: 벽돌집   사용자 컬렉션 클래스를 작성하면 캡슐화의 견고함을 제공하고 덧붙여 For Each Next를 다시 사용할 수 있게 됩니다.

     
  • 우수한 개체 지향 디자인으로 얻는 이익   Collection 클래스를 사용하면 더욱 완벽하고 안전한 코드가 됩니다.

 

공용 컬렉션 예제: 초가집

예제를 작성하려면 새 프로젝트를 열고 두 클래스 모듈을 삽입합니다. 그림 9.13과 같이 다섯 개의 명령 단추, 목록 상자, 두 입력란, 폼의 두 레이블 등을 그립니다.

그림 9.13   Employees 컬렉션 예제

다음은 이 예제 설정에 필요한 속성값을 나열하는 표입니다.

개체

속성

설정

클래스 모듈

Name

Employee

클래스 모듈

Name

SmallBusiness

Caption

Employees 컬렉션

첫째 command button

Caption
Name

추가
cmdAddEmployee

둘째 command button

Caption
Name

삭제
cmdDeleteEmployee

셋째 command button

Caption
Name

목록 새로 고침(Refresh List)
cmdListEmployees

넷째 command button

Caption
Name

오류(Trouble)
cmdTrouble

다섯째 command button

Caption
Name

닫기
cmdClose

첫째 label 컨트롤

Caption

이름

둘째 label 컨트롤

Caption

급여

첫째 text box

Name
Text

TxtName
(
공백)

둘째 text box

Name
Text

txtSalary
(
공백)

List Box

Name

LstEmployees


 

Employee 클래스 모듈에서 다음 선언과 속성 프로시저를 추가합니다.

Option Explicit
' mployee 클래스의 속성
Public Name As String
Public Salary As Long   
 
' 한 번 쓰기 ID 속성의 전용 데이터
Private mstrID As String
 
Property Get ID() As String
   ID = mstrID
End Property
 
' 처음 ID 속성이 설정되면 정적 부울도 설정됩니다.
' 다음 호출은 아무 것도 수행하지 않습니다.
' 대신 오류를 일으키는 것이 더 좋을 것입니다.
Property Let ID(strNew As String)
   Static blnAlreadySet As Boolean
   If Not blnAlreadySet Then
      blnAlreadySet = True
      mstrID = strNew
   End If
End Property

ID 속성은 컬렉션에서 Employee 개체를 검색하거나 삭제하는 키이므로 한 번 설정하고 나면 절대 변경하면 안됩니다. 처음 속성이 설정될 때 True로 설정된 Static 부울 변수와 함께 완성됩니다. Property Get이 있기 때문에 속성을 언제든지 읽을 수 있습니다.

SmallBusiness 클래스 모듈에 다음 선언을 추가합니다. 컬렉션 개체는 처음 Employees 변수가 코드에서 참조될 때 작성됩니다.

Option Explicit
Public Employees As New Collection

모든 작업을 수행하는 폼

여분의 코드는 모두 폼 모듈로 이동합니다. 선언 영역에 다음 선언을 추가합니다.

Option Explicit
Public sbMain As New SmallBusiness

cmdEmployeeAdd_Click 이벤트의 코드는 컬렉션에 구성원을 추가합니다.

Private Sub cmdEmployeeAdd_Click()
   Dim empNew As New Employee
   Static intEmpNum As Integer
   ' With를 사용하여 코드를 더 빠르고 간결하게
   ' 만듭니다(.ID empNew.ID).
   With empNew
      ' 새 직원의 고유 ID를 만듭니다.
      intEmpNum = intEmpNum + 1
      .ID = "E" & Format$(intEmpNum, "00000")
      .Name = txtName.Text
      .Salary = CDbl(txtSalary.Text)
      ' ID 속성을 키로 사용하여 컬렉션에
      ' Employee 개체 참조를 추가합니다.
      sbMain.Employees.Add empNew, .ID
   End With
   txtName.Text = ""
   txtSalary.Text = ""
   ' 목록 새로 고침 단추를 누릅니다.
   cmdListEmployees.Value = True
End Sub

cmdListEmployees_Click 이벤트 프로시저의 코드는 For Each ... Next 문을 사용하여 ListBox 컨트롤에 모든 직원 정보를 추가합니다.

Private Sub cmdListEmployees_Click()
   Dim emp As Employee
   lstEmployees.Clear
   For Each emp In sbMain.Employees
      lstEmployees.AddItem emp.ID & ", " & emp.Name _
      & ", " & emp.Salary
   Next
End Sub

cmdEmployeeDelete_Click 이벤트는 Collection 개체의 Remove 메서드를 사용하여 현재 ListBox 컨트롤에 선택된 컬렉션 구성원을 삭제합니다.

Private Sub cmdEmployeeDelete_Click()
   ' 선택된 직원이 있는지 확인합니다.
   If lstEmployees.ListIndex > -1 Then
      ' 처음 여섯 문자가 ID입니다.
      sbMain.Employees.Remove _
      Left(lstEmployees.Text, 6)
   End If
   ' 목록 새로 고침 단추를 누릅니다.
   cmdListEmployees.Value = True
End Sub

다음 코드를 오류 단추에 추가합니다.

Private Sub cmdTrouble_Click()
   ' 뭐라고 말씀해보세요?
   sbMain.Employees.Add Me
End Sub

cmdClose_Click 이벤트는 응용 프로그램을 종료합니다. 개체를 사용하는 프로젝트를 닫을 때 클래스 모듈의 Terminate 이벤트 프로시저가 실행되는지 확인하기 위해 모든 폼을 지우면 됩니다. 이와 반대로 End 문을 사용하면 Terminate 이벤트를 실행하지 않고 프로그램을 갑자기 중지합니다.

Private Sub cmdClose_Click()
   Unload Me
End Sub

예제에서 직원을 추가하려면 응용 프로그램을 실행하고 두 입력란에 값을 입력한 다음 추가 단추를 선택합니다. 직원을 몇 명 추가한 다음 삭제와 목록 단추로 실험해 봅니다.

초가집의 견고함

이 간단한 구현은 별로 견고하지 않습니다. Employees 속성이 공용 Collection 개체이기 때문에 프로그램의 아무 곳에서나 우연히 액세스할 수 있습니다. 게다가 Collection 개체의 Add 메서드는 아무런 형식 확인도 수행하지 않습니다. 예를 들어 오류 단추 Click 이벤트의 코드는 폼에 대한 개체 참조를 직원 컬렉션에 삽입해 버립니다.

오류 단추를 누르고 발생한 오류가 없는지 주의합니다. 이제 목록 새로 고침 단추를 누릅니다. For Each ... Next 루프에서 예기치 않은 개체 유형을 만나면 13번 오류(형식이 일치하지 않습니다.)가 발생합니다.

다음은 공용 Collection 개체와 함께 개체 모델을 만들 때 나타나는 오류 종류를 보여주는 예제입니다. 개체는 프로젝트의 아무 곳에서나 추가할 수 있고 알맞게 초기화된다는 보장이 없습니다. 프로그래머가 직원을 추가하기 위해 코드 사본을 만든 후 나중에 원본 코드가 변경될 경우 생기는 오류는 찾아내기가 매우 어렵습니다.

추가 정보   이 항목의 예제는 "전용 컬렉션 예제: 나무집"에서 계속됩니다.

 

전용 컬렉션 예제: 나무집

이 항목은 "공용 컬렉션 예제: 초가집"의 코드 예제가 계속되는 것입니다. 이 항목을 시작하기 전에 이전 항목을 읽으면 도움이 될 것입니다.

Employee 개체를 SmallBusiness 개체와 좀 더 견고하게 연결하는 방법은 Collection 개체를 전용으로 설정하는 것입니다. 다음 예제에서는 폼과 "전용 Collection" 예제의 코드 대부분을 다시 사용합니다.

Employee 클래스 모듈은 변경되지 않습니다. 그러나 SmallBusiness 클래스 모듈은 완성된 디자인 변경을 얻습니다. 공용 Collection 개체 선언을 다음 선언으로 바꾸고 다음 단락에 설명된 Sub Function 프로시저를 추가합니다.

Option Explicit
Private mcolEmployees As New Collection

직원을 추가하는 코드는 대부분의 작업을 수행합니다. 이전 예제의 cmdEmployeeAdd_Click 이벤트 프로시저 밖의 점선 사이 코드 블록을 사용할 수 있습니다.

중요한 변경 사항은 mcolEmployees Private이기 때문에 Collection 개체의 Add 메서드를 더 이상 프로그램의 모듈에서 호출할 수 없다는 것입니다. 새 개체를 올바르게 초기화하는 EmployeeAdd 메서드를 사용하는 Employee 개체만 추가할 수 있습니다.

' SmallBusiness 클래스의 메서드
Public Function EmployeeAdd(ByVal Name As String, _
ByVal Salary As Double) As Employee
   ' - - - - - - - - - - - - - - - -
   Dim empNew As New Employee
   Static intEmpNum As Integer
   ' With를 사용하여 코드를 더 빠르고 간결하게
   ' 만듭니다(.ID empNew.ID).
   With empNew
      ' 새 직원의 고유 ID를 만듭니다.
      intEmpNum = intEmpNum + 1
      .ID = "E" & Format$(intEmpNum, "00000")
      .Name = Name
      .Salary = Salary
      ' ID 속성을 키로 사용하여 컬렉션에
      ' Employee 개체 참조를 추가합니다.
      ' - - - - - - - - - - - - - - - -
      mcolEmployees.Add empNew, .ID
   End With
   '  Employee에 참조를 반환합니다.
   Set EmployeeAdd = empNew
End Function

EmployeeAdd 메서드는 새로 추가된 Employee 개체에 대한 참조를 반환합니다. 개체를 작성하자 마자 개체로 무슨 작업이든 수행할 것이므로 좋은 연습이 될 것입니다.

EmployeeCount, EmployeeDelete, Employees 등의 메서드는 Collection 개체의 해당 메서드에 위임합니다. 위임은 Collection 개체에서 모든 작업을 수행함을 의미합니다.

' SmallBusiness 클래스의 메서드
Public Function EmployeeCount() As Long
   EmployeeCount = mcolEmployees.Count
End Function
 
Public Sub EmployeeDelete(ByVal Index As Variant)
   mcolEmployees.Remove Index
End Sub
 
Public Function Employees(ByVal Index As Variant) _
As Employee
   Set Employees = mcolEmployees.Item(Index)
End Function

메모   이 메서드에 기능을 추가할 수 있습니다. 예를 들어 인덱스가 유효하지 않을 때 사용자 오류를 발생시킬 수 있습니다.

마지막 메서드는 Trouble입니다. 이 메서드는 초기화되지 않은 Employee 개체를 컬렉션에 추가하려고 합니다. 어떤 일이 일어날 지 짐작할 수 있습니까?

' SmallBusiness 클래스의 메서드
Public Sub Trouble()
   Dim x As New Employee
   mcolEmployees.Add x
End Sub

폼으로 변경

폼 모듈을 약간 변경해야 합니다. 이전 예제에 사용된 같은 모듈 수준의 선언을 사용할 수 있고 닫기 단추의 Click 이벤트가 같지만 나머지 이벤트 프로시저가 변경되었습니다. 삭제 단추와 직원 목록(List Employees) 단추 코드가 더 작지만 의미 있는 방법으로 변경된 반면 추가 단추 코드는 더 짧아집니다.

Private Sub cmdEmployeeAdd_Click()
   sbMain.EmployeeAdd txtName.Text, txtSalary.Text
   txtName.Text = ""
   txtSalary.Text = ""
   cmdListEmployees.Value = True
End Sub
 
Private Sub cmdEmployeeDelete_Click()
   ' 선택된 직원이 있는지 확인합니다.
   If lstEmployees.ListIndex > -1 Then
      ' 처음 여섯 문자는 ID입니다.
      sbMain.EmployeeDelete Left(lstEmployees.Text, 6)
   End If
   cmdListEmployees.Value = True
End Sub
 
Private Sub cmdListEmployees_Click()
   Dim lngCt As Long
   lstEmployees.Clear
   For lngCt = 1 To sbMain.EmployeeCount
      With sbMain.Employees(lngCt)
         lstEmployees.AddItem .ID & ", " & .Name _
         & ", " & .Salary
      End With
   Next
End Sub

cmdListEmployees_Click에서 이 추가 코드가 전부가 아닙니다. 불행하게도 Collection 개체는 현재 Private로 선언되었기 때문에 견고함을 얻기 위해 컬렉션의 항목을 통해 반복하는데 For Each ... Next를 사용할 수 있는 능력을 포기했습니다. 다음을 코드로 작성하면 오류가 일어납니다.

' Employees가 사실은 컬렉션이 아니기 때문에
' 작동하지 않습니다.
For Each emp In sbMain.Employees

다행히도 EmployeeCount 메서드는 반복 계산 영역을 구분하는데 사용할 수 있습니다.

오류 단추도 약간 변경되지만 여전히 잘 작동합니다.

Private Sub cmdTrouble_Click()
   sbMain.Trouble
End Sub

프로젝트를 실행하고 추가, 삭제, 목록 새로 고침 등의 단추로 실험해 보십시오. 모든 것이 전과 같이 작동할 것입니다.

오류 단추를 누르면 다시 한번 오류가 생깁니다. 그러나 목록 새로 고침 단추를 누르면 초기화되지 않은 Employee 개체에 컬렉션이 추가되어 있는 것을 확인할 수 있습니다.

어떻게 그럴 수 있을까요? Collection 개체를 전용으로 설정함으로써 SmallBusiness 개체 외부에 있는 프로그램의 모든 코드로부터 보호하지만 코드 내부로부터는 보호하지 않습니다. 다량의 코드가 있는 SmallBusiness 개체는 크고 복잡할 수 있습니다. 예를 들어 CustomerAdd, ProductAdd 등과 같은 메서드가 있을 것입니다.

전용 변수는 클래스 모듈을 통해 보이기 때문에 코드 작성 오류나 EmployeeAdd 메서드의 사본 작성은 여전히 컬렉션에 삽입되고 있는 잘못된 데이터(심지어 유효하지 않은 개체)로 끝날 수 있습니다.

추가 정보   이 예제는 "사용자 컬렉션 클래스 작성: 벽돌집"에서 계속됩니다.

 

사용자 컬렉션 클래스 작성: 벽돌집

이 항목은 "공용 컬렉션 예제: 초가집" "전용 컬렉션 예제: 나무집"의 코드 예제가 계속되는 것입니다. 이 항목을 시작하기 전에 이전 항목을 읽으면 도움이 될 것입니다.

가장 견고하게 컬렉션을 구현하는 방법은 클래스 모듈로 만드는 것입니다. 이전 예제와 대조를 이루어 개체 작성을 위해 모든 코드를 컬렉션 클래스로 이동하면 좋은 개체 디자인 원칙을 따르게 됩니다.

다음 예제에서는 같은 폼과 이전 예제와 같은 Employee 클래스 모듈을 사용합니다. 새 클래스 모듈을 삽입하고 Name 속성을 "Employees"로 설정한 후 다음 선언과 코드를 새 클래스 모듈에 삽입합니다.

Option Explicit
Private mcolEmployees As New Collection

Employees 클래스의 Add, Count, Delete 메서드는 특히 기존 SmallBusiness 클래스의 메서드와 같습니다. SmallBusiness 클래스 모듈에서 간단히 삭제하고 Employees 클래스 모듈에 붙여넣고 이름을 변경할 수 있습니다.

더 이상 CustomerAdd EmployeeAdd를 구별할 필요가 없기 때문에 이름을 변경할 수 있습니다. 구현하는 각 컬렉션 클래스에는 고유의 Add 메서드가 있습니다.

' Employees 컬렉션 클래스의 메서드
Public Function Add(ByVal Name As String, _
ByVal Salary As Double) As Employee
   Dim empNew As New Employee
   Static intEmpNum As Integer
   ' With를 사용하여 코드를 더 빠르고 간결하게
   ' 만듭니다(.ID empNew.ID).
   With empNew
      ' 새 직원의 고유 ID를 만듭니다.
      intEmpNum = intEmpNum + 1
      .ID = "E" & Format$(intEmpNum, "00000")
      .Name = Name
      .Salary = Salary
      ' ID 속성을 키로 사용하여 컬렉션에
      ' Employee 개체 참조를 추가합니다.
      mcolEmployees.Add empNew, .ID
   End With
   '  Employee에 참조를 반환합니다.
   Set Add = empNew
End Function
 
Public Function Count() As Long
   Count = mcolEmployees.Count
End Function
 
Public Sub Delete(ByVal Index As Variant)
   mcolEmployees.Remove Index
End Sub

SmallBusiness 개체의 Employees 메서드는 컬렉션 클래스의 Item 메서드가 되고 인덱스나 키로 구성원을 검색하기 위해 Collection 개체에 계속 위임합니다.

' Employees 컬렉션 클래스의 메서드
Public Function Item(ByVal Index As Variant) _
As Employee
   Set Item = mcolEmployees.Item(Index)
End Function

여기 추가할 수 있는 좋은 방법이 있습니다. Item Employees 클래스의 기본 메서드로 설정함으로써 Collection 개체에서 하던 대로 Employees("E00001")를 코딩할 수 있게 됩니다.

Item을 기본 속성으로 설정하려면

  1. [도구] 메뉴에서 [프로시저 특성]을 눌러 [프로시저 특성] 대화 상자를 엽니다. [이름] 상자에서 Item 메서드를 선택합니다.

     
  2. [고급]을 눌러 고급 기능을 나타냅니다. [프로시저 ID] 상자에서 [(기본값)]을 눌러 Item 메서드를 기본값으로 설정한 다음 [확인]을 누릅니다.

메모   클래스는 기본 구성원(속성이나 메서드)을 하나만 가질 수 있습니다.

For Each … Next 사용

For Each … Next를 견고하게 다시 사용할 수 있습니다. 다음 메서드를 추가함으로써 다시 Collection 개체에 모든 작업을 위임할 수 있습니다.

' NewEnum은 컬렉션 열거자의
' Iunknown 인터페이스를 반환해야 합니다.
Public Function NewEnum() As IUnknown
   Set NewEnum = mcolEmployees.[_NewEnum]
End Function

Collection 개체에 위임하고 있는 중요한 것은 그 열거자입니다. 열거자는 컬렉션에서 항목을 통해 반복하는 방법을 아는 작은 개체입니다. Visual Basic으로 열거자 개체를 작성할 수 없지만 Employees 클래스는 Collection 개체에 기초하기 때문에 Collection 개체의 항목 열거 방법을 원래 알고 있는 Collection 개체의 열거자를 반환할 수 있습니다.

Collection 개체의 _NewEnum 메서드 주위 대괄호는 메서드 이름 앞에 오는 밑줄 때문에 필요합니다. 앞에 오는 이 밑줄은 메서드가 형식 라이브러리에 숨겨져 있음을 나타내는 규정입니다. 메서드 _NewEnum의 이름을 지정할 수는 없지만 형식 라이브러리에 숨기고 For Each … Next에 필요한 프로시저 ID를 지정할 수는 있습니다.

NewEnum 메서드를 숨기고 필요한 프로시저 ID를 제공하려면

  1. [도구] 메뉴에서 [프로시저 특성]을 눌러 [프로시저 특성] 대화 상자를 엽니다. [이름] 상자에서 NewEnum 메서드를 선택합니다.

     
  2. [고급]을 눌러 고급 기능을 나타냅니다. [구성원 숨기기]를 눌러 형식 라이브러리에서 NewEnum를 숨깁니다.

     
  3. [프로시저 ID] 상자에 4를 입력하여 For Each Next에 필요한 프로시저 ID NewEnum에 제공한 다음 [확인]을 누릅니다.

중요   컬렉션 클래스에서 For Each … Next와 함께 작업하려면 숨겨진 NewEnum 메서드에 적절한 프로시저 ID를 제공해야 합니다.

얼마 남지 않은 SmallBusiness 클래스

SmallBusiness 클래스에는 현재 코드가 별로 없을 것입니다. Collection 개체와 삭제한 모든 메서드를 대체할 새 선언과 읽기 전용 속성이 있습니다.

Option Explicit
Private mEmployees As New Employees
 
Public Property Get Employees() As Employees
   Set Employees = mEmployees
End If

이것은 설명해야 합니다. Property Get를 생략한 채 단지 Public Employees As New Employees로 선언했을 경우입니다.

모두 실수하지 않으면 모든 것이 잘 진행되겠지만 실수로 Set sbMain.Employees = Nothing을 코딩했을 경우 어떻게 될까요? Employees 컬렉션이 소멸될 것입니다. Employees를 읽기 전용 속성으로 설정하면 그럴 가능성을 피할 수 있습니다.

폼으로 변경

폼 모듈에 대한 코드는 앞의 예제와 매우 비슷합니다. 같은 모듈 수준의 선언을 사용할 수 있고 닫기 단추의 Click 이벤트가 같습니다.

대부분의 이벤트 프로시저에서 유일하게 변경되는 것은 SmallBusiness 클래스의 기존 메서드를 Employees 컬렉션 개체의 새 메서드로 대체하는 것뿐입니다.

Private Sub cmdEmployeeAdd_Click()
   sbMain.Employees.Add txtName.Text, txtSalary.Text
   txtName.Text = ""
   txtSalary.Text = ""
   cmdListEmployees.Value = True
End Sub
 
Private Sub cmdEmployeeDelete_Click()
   ' 선택된 직원이 있는지 확인합니다.
   If lstEmployees.ListIndex > -1 Then
      ' 처음 여섯 문자가 ID입니다.
      sbMain.Employees.Delete _
      Left(lstEmployees.Text, 6)
   End If
   cmdListEmployees.Value = True
End Sub
 
Private Sub cmdListEmployees_Click()
   Dim emp As Employee
   lstEmployees.Clear
   For Each emp In sbMain.Employees
      lstEmployees.AddItem emp.ID & ", " & emp.Name _
      & ", " & emp.Salary
   Next
End Sub

For Each … Next를 다시 사용하여 직원을 나열할 수 있습니다.

프로젝트를 실행하고 모두 제대로 작동되는지 확인합니다. 여기서는 캡슐화에서 문제를 해결했기 때문에 오류 단추에 해당되는 코드가 없습니다.

추가 정보   컬렉션에서 배경은 "Visual Basic Collection 개체" "Visual Basic 컬렉션"을 참조하십시오.

Professional Enterprise Edition에 포함된 클래스 작성기 유틸리티로 컬렉션 클래스를 작성할 수 있습니다.

초가집, 나무집, 벽돌집의 예제 학습은 "우수한 개체 지향 디자인으로 얻는 이익"에 요약되어 있습니다.

 

우수한 개체 지향 디자인으로 얻는 이익

이 항목은 "공용 컬렉션 예제: 초가집"에서 시작하고 "전용 컬렉션 예제: 나무집" "사용자 컬렉션 클래스 작성: 벽돌집"에서 계속된 코드 예제의 결과를 요약한 것입니다. 이 항목을 시작하기 전에 이전 항목을 읽으면 도움이 될 것입니다.

Employees 컬렉션 클래스를 작성하면 매우 완벽한 모듈적 코드 작성 형식이 됩니다. 모든 컬렉션 코드는 SmallBusiness 클래스 모듈의 크기를 줄이는 컬렉션 클래스(캡슐화)에 있습니다. Employee 개체의 컬렉션이 개체 계층의 여러 위치에 나타날 경우 컬렉션 클래스를 재사용하려면 코드가 중복되지 않아야 합니다.

Collection 클래스 향상

컬렉션 클래스의 메서드와 속성을 추가로 구현할 수 있습니다. 예를 들어 Copy Move 메서드 또는 SmallBusiness 개체 참조가 있는 읽기 전용 Parent 속성을 구현할 수 있습니다.

또한 이벤트를 추가할 수도 있습니다. 예를 들어 Add Remove 메서드에서 컬렉션의 항목 수를 변경할 때마다 CountChanged 이벤트를 로드할 수 있습니다.

견고함, 견고함, 견고함

항상 가장 견고한 방법으로 컬렉션을 구현할 필요는 없습니다. 그러나 개체를 사용한 프로그래밍의 좋은 점 중 하나는 코드를 재사용할 수 있다는 것입니다. 원본 코드를 복사하는 것보다 개체를 재사용하는 것이 더 쉽고 견고하고 캡슐화된 코드를 사용하는 것이 더 안전합니다.

"진짜 견고한 코드를 기록하려면 정말로 나쁜 일이 일어날 것을 가정해야 한다"고 합니다.

Collection 클래스와 구성 요소 프로그램

Visual Basic Professional Enterprise Edition을 사용할 경우 작성한 개체를 회사의 다른 프로그래머가 사용할 수 있도록 프로젝트를 ActiveX 구성 요소로 바꿀 수 있습니다.

Collection 클래스 구현 단계

다음 목록에서는 컬렉션 클래스 작성에 필요한 단계를 요약한 것입니다.

  1. 프로젝트에 클래스 모듈을 추가하고 이름(보통 컬렉션 클래스가 포함할 개체 이름의 복수형)을 지정합니다. 이 장 앞부분의 "속성, 메서드, 이벤트 이름 짓기"를 참고하십시오.

     
  2. 속성과 메서드를 위임할 Collection 개체에 대한 참조를 포함하도록 전용 변수를 추가합니다.

     
  3. Class_Initialize 이벤트 프로시저에서 Collection 개체를 작성합니다. 필요할 때까지 이 개체 작성을 연기하려면 단계 2에서 전용 변수를 As New Collection으로 선언합니다. 이것은 Collection이 액세스될 때마다 소량의 오버헤드를 추가합니다.

     
  4. Count 속성과 Add, Item, Remove 메서드를 클래스 모듈에 추가합니다. 각 경우에 해당 구성원을 호출하여 전용 Collection에 위임합니다.

     
  5. Add 메서드를 구현할 때 한 종류의 개체만 허용하여 Collection 개체의 민감하지 않은 Add 메서드 동작을 무시할 수 있습니다. Add 메서드에서 개체의 작성과 초기화를 완전히 제어하도록 외부에서 작성된 개체를 컬렉션에 추가할 수도 있습니다.

     
  6. [프로시저 특성] 대화 상자에서 컬렉션 클래스의 Item 메서드를 기본값으로 설정합니다.

     
  7. 아래와 같이 NewEnum 메서드를 추가합니다. [프로시저 특성] 대화 상자에서 숨김으로 표시하고 For Each Next와 함께 작업할 수 있도록 프로시저 ID 4를 제공합니다.
8.     Public Function NewEnum() As IUnknown
9.     Set NewEnum = mcol.[_NewEnum]
10.   End Function

메모   위의 코드는 단계 2의 전용 변수 이름을 mcol로 가정합니다.

  1. 사용자 정의 속성, 메서드, 이벤트를 컬렉션 클래스에 추가합니다.

메모   Visual Basic Professional Enterprise Edition에 포함된 클래스 작성기 유틸리티로 컬렉션 클래스를 작성할 수 있습니다. 결과로 나온 원본 코드를 사용자 정의할 수 있습니다.

추가 정보   소프트웨어의 구성 요소에 대한 자세한 내용은 구성 요소 안내서ActiveX 구성 요소 작성을 참조하십시오.

 

개체 모델

일단 클래스 모듈을 작성하고 속성과 메서드를 지정하여 클래스를 정의했다면 해당 클래스에서 원하는 만큼 개체를 작성할 수 있습니다. 작성한 개체를 어떻게 추적하시겠습니까?

개체를 추적하는 가장 간단한 방법은 작성할 각 개체에 개체 변수를 선언하는 것입니다. 물론 작성할 수 있는 개체 수에는 제한이 있습니다.

이 장 앞부분의 "개체 배열 작성" "개체 컬렉션 작성"의 설명처럼 배열이나 컬렉션에서 다중 개체 참조를 유지할 수 있습니다.

처음에는 일반 변수에서 하는 것처럼 폼이나 표준 모듈에 개체 변수, 배열, 컬렉션 등을 추가할 것입니다. 그래도 클래스를 더 추가하면 사용 중인 개체간에 명백한 관계를 가진다는 것을 알게 될 것입니다.

포함 관계를 표현하는 개체 모델

개체 모델은 개체 기반 프로그램에 구조를 제공합니다. 프로그램에서 사용하는 개체간 관계를 정의함으로써 개체 모델은 프로그래밍을 쉽게 만드는 방법으로 개체를 관리합니다.

보통 개체 모델은 일부 개체가 다른 개체보다 "더 크거나" 더 중요함을 표현합니다. 이 때 다른 개체란 자신이 아닌 개체를 포함하거나 다른 개체로 구성된 것으로 생각할 수 있습니다.

예를 들어 SmallBusiness 개체를 프로그램 핵심으로 작성할 수 있습니다. Employee 개체와 Customer 개체처럼 SmallBusiness 개체에서 연관된 다른 개체 유형을 가질 수 있습니다. Product 개체를 포함할 수도 있습니다. 이 프로그램의 개체 모델은 그림 9.11에 나타나 있습니다.

그림 9.11   개체 모델

SmallBusiness, Employee, Customer, Product 등의 이름을 가진 네 개의 클래스 모듈을 정의하고 각각 적절한 속성과 메서드를 부여할 수 있지만 각각의 개체를 어떻게 연결하시겠습니까? 이런 목적에 사용할 수 있는 두 가지 도구가 있습니다. 다음 코드는 그림 9.11의 계층을 구현하는 한 방법을 나타냅니다.

' SmallBusiness 클래스 모듈의
' 선언 영역 코드
Public Name As String
Public Product As New Product
Public Employees As New Collection
Public Customers As New Collection

처음 Product 속성을 참조하면 As New로 선언되기 때문에 개체가 작성됩니다. 예를 들어 다음 코드는 SmallBusiness 개체의 Product 개체 이름과 가격을 작성하고 설정합니다.

' 표준 모듈 코드
Public sbMain As New SmallBusiness
Sub Main
   sbMain.Name = "Velociraptor Enterprises, Inc."
   ' 처음 Product 변수가 코드에서 사용될 때
   ' Product 개체가 작성됩니다.
   sbMain.Product.Name = "Inflatable Velociraptor"
   sbMain.Product.Price = 1.98
   .
   .   ' 기본 폼을 초기화하고 나타내는 코드
   .
End Sub

메모   공용 변수로 개체 속성을 구현하는 것은 조잡한 방법입니다. 코드에서 속성을 Nothing으로 설정하여 실수로 Product 개체를 없앨 수 있습니다. 다음 코드와 같이 개체 속성을 읽기 전용 속성으로 작성하는 것이 좋습니다.

' 견고한 개체 속성 코드
' 속성이 전용인 저장 공간입니다. 따라서 개체 외부에서
' Nothing으로 설정할 수 없습니다.
Private mProduct As New Product
 
Property Get Product() As Product
   ' 처음 이 속성이 호출되면
   ' mProduct Nothing을 포함하므로
   ' Visual Basic에서 Product 개체를 작성합니다.
   Set Product = mProduct
End If

일대다 개체 관계

개체 사이의 관계가 일대일이면 개체 속성은 바르게 작동합니다. 그러나 한 형식의 개체에 다른 형식의 여러 개체가 있는 경우가 자주 발생합니다. SmallBusiness 개체 모델에서 Employees 속성은 SmallBusiness 개체가 여러 Employee 개체를 포함할 수 있도록 Collection 개체로 구현됩니다. 다음 코드는 새 Employee 개체가 이 컬렉션에 추가되는 방법을 나타냅니다.

Public Function NewEmployee(Name, Salary, HireDate, _
ID) As Employee
   Dim empNew As New Employee
   empNew.Name = Name      ' 내부적인 개체 작성
   empNew.Salary = Salary
   empNew.HireDate = HireDate
   ' ID를 키로 사용하여 컬렉션에 추가합니다.
   sbMain.Employees.Add empNew, CStr(ID)
   '  Employee 참조를 반환합니다.
   Set NewEmployee = empNew
End Function

NewEmployee 함수는 SmallBusiness 개체에서 나타내는 사업체의 직원을 작성할 필요가 있을 때마다 호출됩니다. 기존 직원은 Employees 컬렉션에서 반복하여 언제라도 나열할 수 있습니다.

메모   다시 한번 말하지만 이것은 그다지 견고한 구현이 아닙니다. 더 좋은 연습은 사용자 컬렉션 클래스를 작성하고 읽기 전용 속성으로 드러내는 것입니다. 자세한 정보는 "사용자 고유 컬렉션 클래스 작성"을 참조하십시오.

   Visual Basic Professional Enterprise Edition에 포함된 클래스 작성기 유틸리티는 개체 모델을 구현하는데 필요한 만큼 코드를 작성할 수 있습니다. 클래스 작성기로 견고한 개체 속성과 컬렉션 클래스를 작성하고 모델을 쉽게 재배열할 수 있습니다.

Parent 속성

개체 참조가 있으면 그 개체 속성과 컬렉션을 사용하여 포함하는 개체에 도달할 수 있습니다. 또한 참조하는 개체를 포함하는 개체에 도달하도록 계층 구조의 윗부분으로 탐색하는데 매우 유용하게 사용할 수 있습니다.

위로 탐색하는 것은 보통 Parent 속성과 함께 수행됩니다. Parent 속성은 개체 컨테이너에 대한 참조를 반환합니다. 개체 모델 이동에 대한 자세한 내용은 "구성 요소를 사용한 프로그래밍" "개체 모델 탐색"을 참조하십시오.

이 장 앞부분의 "속성을 클래스에 추가"에서 Parent 속성의 예제를 참고할 수 있습니다.

   컬렉션에서 Parent 속성을 개체에 할당할 때 Collection 개체 참조를 사용하지 않는 것이 좋습니다. 실제 개체의 상위는 컬렉션이 있는 개체입니다. Parent 속성이 컬렉션을 가리키면 실제 상위를 얻기 위해 거짓으로 두 수준을 사용해야 합니다(, obj.Parent 대신 obj.Parent.Parent).

Parent 속성, 순환 참조, 개체 해체

Parent 속성의 가장 큰 문제점은 순환 참조를 작성한다는 것입니다. "더 큰" 개체에는 포함하는 개체 참조가 있고 포함된 개체에는 그림 9.12의 루프를 작성하는 Parent 속성을 통한 참조가 있습니다.

그림 9.12   순환 참조의 경우

이 그림에서 무엇이 잘못되었습니까? 개체 작업을 했을 때 개체를 제거하는 방법은 모든 개체 참조를 해제하는 것입니다. 이 항목의 앞부분처럼 SmallBusiness 개체 참조가 sbMain 변수에 있다고 가정하면 다음 코드를 기록할 수 있습니다.

Set sbMain = Nothing

불행하게도 여전히 SmallBusiness 개체 참조가 있습니다. 사실 각 Employee 개체의 Parent 속성은 SmallBusiness 개체 참조를 가지고 있기 때문에 많은 참조가 있을 수 있습니다.

SmallBusiness 개체의 Employees 컬렉션은 각 Employee 개체 참조를 가지고 있기 때문에 모든 개체가 소멸되지 않습니다.

TearDown 메서드

한 가지 해결책은 SmallBusiness 개체에 TearDown 메서드를 제공하는 것입니다. 이 메서드는 모든 SmallBusiness 개체의 개체 속성을 Nothing으로 설정할 수 있고 모든 Collection 개체(Employees, Customers) Nothing으로 설정할 수도 있습니다.

Collection 개체가 소멸되면 Visual Basic에서 가지고 있던 모든 개체 참조를 Nothing으로 설정합니다. Employee Customer 컬렉션에 포함된 Employee Customer 개체에 다른 참조가 없으면 해당 개체가 소멸됩니다.

물론, Employee 개체가 미세한 개체로 구성되었을 경우 상위와 같은 순환 참조 문제를 가집니다. 그런 경우에는 Employee 클래스에 TearDown 메서드를 제공해야 합니다. Employees Collection 개체를 Nothing으로 설정하는 대신 SmallBusiness 개체는 각 Employee 개체의 TearDown 메서드를 호출하면서 컬렉션을 통해 반복해야 합니다.

아직 끝나지 않았습니다

모든 개체가 소멸되지 않을 수 있습니다. SmallBusiness 개체나 포함하는 개체에 대한 참조를 여전히 가지고 있는 프로그램에 변수가 있을 경우 이 개체는 소멸되지 않습니다. 프로그램 정리 부분에서 모든 개체 변수를 Nothing으로 설정했는지 확인해야 합니다.

무엇이 일어나는지 알기 위해 개체에 일부 디버깅 코드를 추가할 수 있습니다. 예를 들어 다음 코드를 표준 모듈에 추가할 수 있습니다.

' 전역 디버그 컬렉션
Public gcolDebug As New Collection
 
' 각 개체에 고유 ID를 제공하는 전역 함수
Public Function DebugSerial() As Long
   Static lngSerial As Long
   lngSerial = lngSerial + 1
   DebugSerial = lngSerial
End Function

각 클래스 모듈에서 다음과 비슷하게 코드를 추가할 수 있습니다. 각 클래스는 "Product"가 나타나는 위치에 고유 이름을 제공합니다.

' 디버그 ID 저장 공간
Private mlngDebugID As Long
 
Property Get DebugID() As Long
   DebugID = mlngDebugID
End Property
 
Private Sub Class_Initialize()
   mlngDebugID = DebugSerial
   ' 전역 컬렉션에 문자열 항목을 추가합니다.
   gcolDebug.Add "Product Initialize; DebugID=" _
   & DebugID, CStr(DebugID)
End Sub
 
Private Sub Class_Terminate()
   ' 개체가 더이상 주위에 없는지 알기 위해
   ' 문자열 항목을 삭제합니다.
   gcolDebug.Remove CStr(DebugID)
End Sub

각 개체가 작성되면 전역 컬렉션에 문자열을 저장합니다. 개체가 소멸되면 문자열을 삭제합니다. 언제라도 소멸된 개체를 확인하도록 전역 컬렉션에서 반복합니다.

추가 정보   ProgWOb.vbg 예제 응용 프로그램에서 다양한 디버깅 기술을 설명합니다. Visual Basic Professional Edition Enterprise Edition을 사용하여 ActiveX 구성 요소를 작성할 때 개체 모델은 새 중요 사항 및 다양한 문제점들을 나타냅니다. 구성 요소 안내서ActiveX 구성 요소 작성에서 "구성 요소 디자인의 일반 원리"를 참조하십시오.

 

배열 대신 컬렉션 사용

개체로 작업을 할 때 컬렉션을 가장 많이 사용하지만 데이터 형식의 작업에서도 컬렉션을 사용할 수 있습니다. 어떤 경우에서는 항목을 컬렉션에 저장하는 것이 배열에 저장하는 것보다 더 효율적입니다.

작고 동적인 항목의 집합을 사용하고 있는 경우 컬렉션을 사용할 경우가 있습니다. 다음 코드 부분은 URL 주소의 목록을 저장하고 화면에 나타낼 수 있도록 컬렉션을 사용하는 방법을 보여줍니다.

' 모듈 수준 컬렉션
Public colURLHistory As New Collection
 
' 지정된 URL 주소를 컬렉션에 
' 추가하기 위한 코드
Private Sub SaveURLHistory(URLAddress As String)
   colURLHistory.Add URLAddress
End Sub
 
' 직접 실행 창에 URL 주소의 목록을 
' 나타내기 위한 코드
Private Sub PrintURLHistory()
   Dim URLAddress As Variant
   For Each URLAddress in colURLHistory
      Debug.Print URLAddress
   Next URLAddress
End Sub

추가 정보   컬렉션 사용에 대한 자세한 내용은 "개체를 사용한 프로그래밍" "사용자 고유 개체 프로그래밍"을 참조하십시오. 배열 사용에 대한 자세한 내용은 "프로그래밍 기초" "배열"을 참조하십시오.

 

Enum을 사용하여 상수 집합 작업

Enum(Enumerations)은 관련 상수 집합으로 작업하거나 상수값을 이름과 관련지을 때 편리한 방법을 제공합니다. 한 주의 요일과 관련하여 정수 상수 집합의 Enum를 선언하고 코드에서 정수값 대신 요일 이름을 사용할 수 있습니다.

표준 모듈이나 공용 클래스 모듈의 선언 영역에서 Enum 문을 사용하여 Enum 형식을 선언하고 Enum을 만들 수 있습니다. Enum 형식은 적절한 키워드를 가진 Private 또는 Public으로 선언할 수 있습니다. 사용 예는 아래와 같습니다.

Private Enum MyEnum

또는

Public Enum MyEnum

기본 설정으로 Enum의 첫째 상수가 0의 값으로 초기화되고 다음 상수는 이전 상수보다 1씩 큰 값으로 초기화됩니다. 예를 들어 아래 Days Enum 0의 값을 가진 Sunday 상수, 1값을 가진 Monday 상수, 2값을 가진 Tuesday 상수 등을 포함하고 있습니다.

Public Enum Days
   Sunday
   Monday
   Tuesday
   Wednesday
   Thursday
   Friday
   Saturday
End Enum

   Visual Basic은 요일에 대한 상수를 가지고 있는 미리 작성된 vbDayOfWeek Enum을 제공합니다. Enum의 미리 정의된 상수를 보려면 코드 창에서 vbDayOfWeek를 입력하고 마침표를 찍습니다. Visual Basic은 자동으로 Enum 상수 목록을 나타냅니다.

대입문을 사용하여 Enum으로 상수값을 명시적으로 지정할 수 있습니다. 음수를 포함하여 Long형 정수값도 지정할 수 있습니다. 예를 들어 오류 상태를 나타내기 위해 0보다 작은 상수를 사용할 수도 있습니다.

아래 Enum에서 Invalid 상수에 –1의 값을 명시적으로 지정하고 Sunday 상수에는 0값을 지정하였습니다. 그것이 Enum의 첫째 상수이기 때문에 Saturday 0값으로 초기화됩니다. Monday의 값은 1(Sunday보다 1이 큰 값), Tuesday의 값은 2입니다.

Public Enum WorkDays
   Saturday
   Sunday = 0
   Monday
   Tuesday
   Wednesday
   Thursday
   Friday
   Invalid = -1
End Enum

메모   Visual Basic Enum 상수값을 Long 형식의 정수로 취급합니다. Enum 상수에 실수값을 지정할 경우 Visual Basic Long 정수로 반올림합니다.

Enum에서 관련 있는 상수의 집합을 정리하여 다른 구문에서 같은 상수 이름들을 사용할 수 있습니다. Days WorkDays Enum의 요일 상수에 동일한 이름을 사용할 수 있습니다.

개별 상수를 사용할 때 불분명한 사용을 피하려면 Enum의 상수 이름을 앞에 붙여줍니다. 아래 코드는 직접 실행 창에 서로 다른 값을 나타내는 Days WorkDays Enum Saturday 상수를 사용합니다.

Debug.Print "Days.Saturday = " & Days.Saturday
Debug.Print "WorkDays.Saturday = " & WorkDays.Saturday

둘째 Enum의 상수값을 지정할 때 한 Enum의 상수값을 사용할 수 있습니다. 예를 들어 아래 WorkDays Enum의 선언은 위의 선언과 동등합니다.

Public Enum WorkDays
   Sunday = 0
   Monday
   Tuesday
   Wednesday
   Thursday
   Friday
   Saturday = Days.Saturday - 6
   Invalid = -1
End Enum

Enum을 선언하고 난 후에 그 형식의 변수를 선언할 수 있습니다. 그런 다음 Enum의 상수값을 저장하는데 그 변수를 사용합니다. 아래 코드는 WorkDays Enum 상수와 연관된 정수값을 저장하기 위해 WorkDays 형식의 변수를 사용합니다.

Dim MyDay As WorkDays
MyDay = Saturday         ' Saturday 0이 됩니다.
If MyDay < Monday Then   ' Monday 1이 됩니다.
                     ' 따라서 Visual Basic은 아래와 같은
                     ' 메시지 상자를 보여줍니다.
   MsgBox "주말입니다. 근무일이 아닙니다."
End If

코드 창의 예제에서 코드 둘째 줄을 입력할 때 Visual Basic은 자동으로 구성원 자동 목록에 있는 WorkDays Enum 상수를 나타냅니다.

그림 8.7   Visual Basic Enum 상수를 자동으로 나타냅니다

상수 Sunday 0이 되기 때문에 예제의 둘째 줄의 "Saturday" "Sunday"로 대체하면 Visual Basic은 메시지 상자를 나타내기도 합니다.

MyDay = Sunday         ' Sunday 0이 됩니다.

메모   Enum 형식으로 선언된 변수에 Enum 상수값을 정상적으로 지정하였더라도 그 변수에 Long 형식의 정수를 지정할 수 있습니다. Enum 상수와 관계가 없는 변수에 값을 지정하더라도 Visual Basic에는 오류가 발생하지 않습니다.

추가 정보   "Enum "을 참조하십시오. 또는 Professional edition Enterprise edition "ActiveX 구성 요소 작성"에서 "명명된 상수를 구성 요소에 제공"을 참조하십시오.

 


'language > VB6' 카테고리의 다른 글

The Sequence of Events  (0) 2010.06.18
블로그 이미지

란마12

,

The Sequence of Events

Driven by Events
Visual Basic is an "event driven" language. What this means is that your coding
will be performed in the sequence that the events occur, as opposed to the
order in which it appears in your form module. It is therefore very important,
if you want to create quality applications, to know which events will happen
when.


Start Up and Shut Down Events
The most logical place to start would be the 6 events that occur when the form
loads up and the 3 events that occur when the form shuts down.
They are, in this sequence:

Start Up:
Form_Initialize
Form_Load
Form_Resize
Form_Activate
Form_GotFocus
Form_Paint
Shut Down:
Form_QueryUnload
Form_Unload
Form_Terminate



This is what happens most of the time. You know, with a normal form in a little
demo program. In many real life situations, things can be very different. Read
on.


The Form_Initialize event always happens, and it always happens first. After
that, unless something dreadful happens, like an END statement or a fatal error
(you cannot Unload at this stage), the next event will be triggered.


That event is always the Form_Load event, without exception. From this point on
you can unload the form, or do all sorts of other stuff to end the program - so
the following 4 events rely on this not happening. Let's assume that you don't
end the program at this stage.


The Form_Resize event will always happen, whether the form is visible,
minimized or maximized, it will happen. The form receives its physical
dimensions even if it is hidden - so after being loaded, it resizes.


The Form_Activate event is a little more picky. It will only happen if the form
becomes the active form in the application. If for example you have loaded
another form in this form's Load event, and that other form is the active one,
this form's Activate event will not happen until it becomes the activated form.
The event will also not be triggered if this form starts as minimized. Only
when the form is restored or maximized, and becomes active, does this event
occur. This event can of course happen any number of times in the lifetime of a
form. While your user jumps from form to form, the Form_Deactivate and
Form_Activate events occur, telling you whether your user is coming or going.


The Form_GotFocus event, like the Activate event, relies on the form being
visible. But even more picky - only when the form gets the focus. This hardly
ever happens, except if the form does not contain any other controls that can
get the focus. For example, if your form contains a command button, the button
gets the focus after the form loads up, and the Form_Gotfocus does not occur.
If you can think of a good use for this event, please let me know.


The Form_Paint event occurs whenever the form, or any part of it, gets redrawn.
So obviously it happens when the form becomes visible after loading up. It also
happens when you drag something over the form - as each part of the form
becomes exposed and is redrawn, the Form_Paint event occurs. Also, if you are
making a form bigger by resizing it, the parts of the form that becomes visible
as you drag it, triggers the Form_Paint event, sometimes several times a second.


Let's look at the three shut down events:
The Form_QueryUnload event is the first indication that the form wants to shut
down. This is triggered by using the VB "Unload" command, or by clicking the
little cross in the top right corner of the form, by pressing Alt-F4 to close
the form, when Windows is shutting down, when the form's MDIForm parent is
shutting down (if applicable), and so on. The only time your form will
disappear without triggering this event is if you pull the plug on your
computer, or a fatal error occurs in Windows or in your application, or if you
use the END statement. This event is commonly used for asking the user "Do you
want to save changes?" and then saving the changes if the user so wishes. You
can cancel the unloading of the form by setting the Cancel parameter to True
(or any non-zero value).


The Form_Unload event confuses a lot of people who all want to know what the
difference is between QueryUnload and Unload. Well, the main difference is that
the QueryUnload event is for asking the user "Do you want to save" and "Are you
sure?", and then tidying up user stuff, like saving changes etc. The Unload
event happens after the user has said "Yeah sure, go ahead" and you have saved
all the changes. Now you are tidying up the system stuff - making sure all
files are closed and all objects are set to nothing. Also making sure that all
other forms are also unloaded. You can Cancel the unloading of the form at this
point as well, although this is seldom done and is not recommended unless doing
so can save the user from potential loss of data.


The Form_Terminate event also happens.




The Other Events
Although starting up and shutting down are by far the most common event
sequences, there are several other very important combinations of events to
take note of, as listed delicately below:

Clicking on a Form:
Form_MouseDown
Form_MouseUp
Form_Click
Double Clicking on a Form:
Form_MouseDown
Form_MouseUp
Form_Click
Form_MouseMove
Form_DblClick
Form_MouseUp
Pressing a Key on a Form
Note that the active control of the form normally receives the Key events,
unless there is no active control, or if the KeyPreview property of the form is
True.
Form_KeyDown
Form_KeyPress
Form_KeyUp
Minimizing then Restoring a Form:
Form_Resize (when minimizing)
Form_Resize (When Restoring. No events occur while the form is minimized.)
Form_Paint
Calling a Message box from a Form
The form does not respond to ANY events while the message box is displayed.
Form_Paint (this only happens if a part of the form becomes visible when the
message box is closed.)
Dragging something onto a Form
If the Form's OLEDropMode property is set to "None", nothing happens, otherwise
the events below occur.
Form_OLEDragOver (occurs continuously while dragging.)
Form_MouseMove (once, when dropping)
Form_OLEDragDrop (once, when dropping)

ThisArticle_Terminate Event
There are of course many more combinations of events that you and your form may
experience, but I believe that the most common ones had been covered here. If
this article triggers enough question and request events, I might just write a
follow up.

'language > VB6' 카테고리의 다른 글

우수한 개체 지향 디자인으로 얻는 이익  (0) 2010.06.18
블로그 이미지

란마12

,