연습: Visual C#으로 구성 요소 제작
구성 요소는 다시 사용이 가능한 코드를 개체 형태로 제공합니다.개체를 만들어 이들의 속성과 메서드를 호출함으로써 구성 요소의 코드를 사용하는 응용 프로그램을 클라이언트라고 합니다.클라이언트는 자체에서 사용하는 구성 요소와 같은 어셈블리에 있을 수도 있고 그렇지 않을 수도 있습니다.
다음 절차는 서로를 기반으로 하여 빌드되므로 이를 수행하는 순서가 중요합니다.
[!참고]
표시되는 대화 상자와 메뉴 명령은 활성 설정이나 버전에 따라 도움말에서 설명하는 것과 다를 수 있습니다.설정을 변경하려면 도구 메뉴에서 설정 가져오기 및 내보내기를 선택합니다.자세한 내용은 Visual Studio 설정을 참조하십시오.
프로젝트 만들기
CDemoLib 클래스 라이브러리 및 CDemo 구성 요소를 만들려면
파일 메뉴에서 새로 만들기와 프로젝트를 차례로 선택하여 새 프로젝트 대화 상자를 엽니다.Visual C# 프로젝트 형식 목록에서 클래스 라이브러리 프로젝트 템플릿을 선택한 다음 이름 상자에 CDemoLib를 입력합니다.
[!참고]
새 프로젝트를 만들 때는 항상 이름을 지정하십시오.이렇게 하면 루트 네임스페이스, 어셈블리 이름 및 프로젝트 이름을 설정할 수 있으며 또한 기본 구성 요소가 올바른 네임스페이스에 놓이게 됩니다.
솔루션 탐색기에서 마우스 오른쪽 단추로 CDemoLib를 클릭한 다음 바로 가기 메뉴에서 속성을 선택합니다.기본 네임스페이스상자에 CDemoLib가 포함된 것을 볼 수 있습니다.
루트 네임스페이스는 어셈블리에서 구성 요소의 이름을 한정하는 데 사용됩니다.예를 들어, 두 어셈블리에서 CDemo이라는 이름의 구성 요소가 제공되는 경우 CDemoLib.CDemo을 사용하여 CDemo 구성 요소를 지정할 수 있습니다.
대화 상자를 닫습니다.
프로젝트 메뉴에서 구성 요소 추가를 선택합니다.
새 항목 추가 대화 상자에서 구성 요소 클래스를 선택한 다음 이름 상자에 CDemo.cs를 입력합니다.추가를 클릭하여 구성 요소를 만듭니다.
클래스 라이브러리에 CDemo라는 구성 요소가 추가됩니다.
솔루션 탐색기에서 CDemo.cs를 마우스 오른쪽 단추로 클릭한 다음 바로 가기 메뉴에서 코드 보기를 클릭합니다.코드 편집기가 열립니다.
: Component는 public partial class CDemo 바로 아래입니다. 이 단원에서는 사용자 정의 클래스가 어떤 클래스로부터 상속을 받을지 지정합니다.기본적으로 구성 요소는 시스템에서 제공하는 Component 클래스로부터 상속을 받습니다.Component 클래스는 디자이너를 사용하는 기능을 포함하여 구성 요소에 많은 기능을 제공합니다.
솔루션 탐색기에서 마우스 오른쪽 단추로 Class1.cs를 클릭한 다음 삭제를 선택합니다.이렇게 하면 클래스 라이브러리에서 제공하는 기본 클래스가 삭제되어 이 연습에 더 이상 사용되지 않습니다.
파일 메뉴에서 모두 저장을 선택하여 프로젝트를 저장합니다.
생성자 및 소멸자 추가
생성자는 구성 요소의 초기화 방법을 제어하고 Finalize 메서드는 구성 요소의 종결 방법을 제어합니다.CDemo 클래스의 생성자 및 Finalize 메서드에 있는 코드는 기존 CDemo 개체의 실행 수를 유지 관리합니다.
CDemo 클래스의 생성자와 소멸자에 대한 코드를 추가하려면
코드 편집기에서 CDemo 클래스 인스턴스의 총 실행 수와 각 인스턴스의 ID 번호를 유지하기 위한 멤버 변수를 추가합니다.
public readonly int InstanceID; private static int NextInstanceID = 0; private static long ClassInstanceCount = 0;
InstanceCount 및 NextInstanceID 멤버 변수가 static로 선언되었기 때문에 이들 멤버 변수는 클래스 수준에서만 존재합니다.이들 멤버에 액세스하는 CDemo의 모든 인스턴스는 같은 메모리 위치를 사용합니다.정적 멤버는 코드에서 CDemo 클래스가 처음 참조될 때 초기화됩니다.즉, CDemo 개체가 처음 생성될 때나 정적 멤버가 처음으로 액세스될 때 멤버가 초기화됩니다.
CDemo 클래스의 기본 생성자인 public CDemo() 및 public CDemo(IContainer container)를 찾습니다.Visual C#에서 모든 생성자는 클래스와 같은 이름을 갖습니다.구성 요소에는 서로 다른 매개 변수를 가진 여러 생성자가 있을 수 있지만 이들은 모두 구성 요소와 같은 이름을 가져야 합니다.
[!참고]
어떤 클라이언트에서 클래스의 인스턴스를 만들 수 있는지는 생성자의 액세스 수준에 따라 결정됩니다.
새 CDemo가 만들어질 때 인스턴스 수를 증가시키고 인스턴스 ID 번호를 설정하기 위해 다음 코드를 public CDemo()에 추가합니다.
[!참고]
항상 InitializeComponent에 대한 호출 다음에 코드를 추가해야 합니다.이 위치에는 구성 요소를 이루는 모든 요소가 초기화되어 있습니다.
InstanceID = NextInstanceID ++; ClassInstanceCount ++;
InstanceID는 readonly 멤버이기 때문에 생성자에서만 설정할 수 있습니다.
[!참고]
다중 스레드에 익숙한 사용자는 InstanceID를 할당하고 NextInstanceID를 증가시키는 작업이 원자 연산이어야 한다는 점을 정확히 지적할 것입니다.이를 비롯한 스레딩 관련 내용은 연습: Visual C#으로 간단한 다중 스레드 구성 요소 만들기를 참조하십시오.
생성자 코드 작성이 끝나면 다음 메서드를 추가합니다.
~CDemo() { ClassInstanceCount --; }
이 메서드를 소멸자라고 하며 클래스 이름 앞에 물결표 문자(~)를 넣어 표시합니다.메모리 관리자에서는 CDemo 개체가 차지하는 메모리를 마지막으로 회수하기 바로 전에 소멸자를 호출합니다.소멸자를 구현함으로써 구성 요소가 메모리에서 제거되기 바로 전에 정리 작업을 수행할 수 있습니다.그러나 이 연습의 뒷부분에서 알게 되겠지만 리소스를 미리 해제하는 데는 충분한 이유가 있습니다.
클래스에 속성 추가
CDemo 클래스에는 단 하나의 속성이 있으며, 이 속성은 클라이언트가 주어진 시간에 메모리에 있는 CDemo 개체 수를 알아낼 수 있도록 해 주는 정적 속성입니다.메서드는 비슷한 방법으로 만들 수 있습니다.
CDemo 클래스의 속성을 만들려면
클라이언트가 CDemo의 인스턴스 수를 검색할 수 있도록 CDemo 클래스에 다음 속성 선언을 추가합니다.
public static long InstanceCount { get { return ClassInstanceCount; } }
구성 요소 테스트
구성 요소를 테스트하려면 이를 사용하는 프로젝트가 필요합니다.이 프로젝트는 실행 단추를 눌렀을 때 시작되는 첫 프로젝트여야 합니다.
CDemoTest 클라이언트 프로젝트를 솔루션의 시작 프로젝트로 추가하려면
파일 메뉴에서 추가를 가리킨 다음 새 프로젝트를 선택하여 새 프로젝트 추가 대화 상자를 엽니다.
Windows 응용 프로그램 프로젝트 템플릿을 선택하고 이름 상자에 CDemoTest를 입력한 다음 확인을 클릭합니다.
솔루션 탐색기에서 마우스 오른쪽 단추로 CDemoTest를 클릭한 다음 바로 가기 메뉴에서 시작 프로젝트로 설정을 클릭합니다.
CDemo 구성 요소를 사용하려면 클라이언트 테스트 프로젝트에 클래스 라이브러리 프로젝트에 대한 참조가 있어야 합니다.참조를 추가한 후에는 구성 요소 사용을 단순화하기 위해 using 문을 테스트 응용 프로그램에 추가하는 것이 좋습니다.
클래스 라이브러리 프로젝트에 참조를 추가하려면
솔루션 탐색기에서 CDemoTest 바로 아래에 있는 참조 노드를 마우스 오른쪽 단추로 클릭한 다음 바로 가기 메뉴에서 참조 추가를 선택합니다.
참조 추가 대화 상자에서 프로젝트 탭을 선택합니다.
CDemoLib 클래스 라이브러리 프로젝트를 두 번 클릭합니다.CDemoLib가 CDemoTest 프로젝트의 참조 노드 아래에 나타납니다.
솔루션 탐색기에서 마우스 오른쪽 단추로 Form1.cs를 클릭한 다음 바로 가기 메뉴에서 코드 보기를 선택합니다.
참조를 CDemoLib에 추가하면 CDemo 구성 요소의 정규화된 이름인 CDemoLib.CDemo를 사용할 수 있습니다.
using 문을 추가하려면
Form1에 대한 코드 편집기의 맨 위에 있는 using 문 목록에 다음 using 문을 추가합니다.
using CDemoLib;
using 문을 추가하면 라이브러리 이름을 생략하고 구성 요소 형식을 CDemo로 참조할 수 있습니다.
이제 테스트 프로그램을 만들고 이를 사용하여 구성 요소를 테스트합니다.
개체 수명 이해
CDemoTest 프로그램에서는 수 많은 CDemo 개체를 만들고 해제하는 방법으로 .NET Framework에 있는 개체 수명을 설명합니다.
CDemo 개체를 만들고 해제하는 코드를 추가하려면
**Form1.cs[디자인]**을 클릭하여 디자이너로 돌아갑니다.
도구 상자의 모든 Windows Forms 탭에 있는 Button 및 Timer를 Form1 디자인 화면으로 끌어 옵니다.
비시각적인 Timer 구성 요소가 폼 아래에 있는 별도의 디자인 화면에 나타납니다.
timer1의 아이콘을 두 번 클릭하여 Timer1 구성 요소의 Tick 이벤트에 대한 이벤트 처리 메서드를 만듭니다.다음 코드를 이벤트 처리 메서드에 포함시킵니다.
this.Text = "CDemo instances: " + CDemo.InstanceCount;
타이머의 단위 간격마다 폼의 캡션에 CDemo 클래스의 현재 인스턴스 수가 표시됩니다.클래스 이름이 정적 InstanceCount 속성의 한정자로 사용되므로 정적 멤버에 액세스하기 위해 CDemo의 인스턴스를 만들 필요가 없습니다.
Form1의 생성자(public Form1())를 찾아 InitializeComponent()에 대한 호출 후에 다음 코드를 추가합니다.
timer1.Enabled = true;
이렇게 하면 폼이 만들어지자마자 타이머가 시작됩니다.
Form1.cs[디자인] 탭을 클릭하여 디자이너로 돌아갑니다.
Form1의 Button을 두 번 클릭하여 단추의 Click 이벤트에 대한 이벤트 처리 메서드를 만들고다음 코드를 이벤트 처리 메서드에 포함시킵니다.
CDemo cd; int ct; for (ct = 0; ct < 1000; ct++) cd = new CDemo();
이 코드가 이상해 보일 것입니다.CDemo의 각 인스턴스가 만들어지면서 이전 인스턴스가 해제됩니다.for 루프가 모두 수행되면 CDemo의 인스턴스가 하나만 남게 됩니다.이벤트 처리 메서드가 끝나면 cd 변수가 범위를 벗어나므로 이 인스턴스마저 해제됩니다.
이미 추측하고 있겠지만 실제로는 이런 식으로 처리되지 않습니다.
CDemoTest 및 CDemo 프로젝트를 실행하고 디버깅하려면
F5 키를 눌러 솔루션을 시작합니다.
클라이언트 프로젝트가 시작되고 Form1이 표시됩니다.폼 캡션에 "CDemo instances: 0"이 표시되는 것을 볼 수 있습니다.
단추를 클릭합니다.폼 캡션에 "CDemo instances: 1000"이 표시되어야 합니다.
CDemo의 인스턴스가 단추의 Click 이벤트 처리 프로시저가 끝날 때에는 모두 해제되어 있습니다.이 인스턴스들이 왜 종결되지 않았을까요?간단히 말하면 메모리 관리자에서 낮은 우선 순위를 사용하여 개체를 백그라운드로 종결하기 때문입니다.우선 순위는 시스템에서 메모리를 적게 사용해야만 높아집니다.이 lazy 가비지 수집 계획을 사용하면 신속하게 개체를 할당할 수 있습니다.
캡션을 보면서 단추를 여러 번 더 클릭합니다.특정 순간에 인스턴스 수가 갑자기 줄어들 것입니다.이것은 메모리 관리자에서 일부 개체의 메모리를 회수했음을 의미합니다.
[!참고]
10번을 초과하여 클릭했는데도 CDemo 인스턴스의 수가 감소하지 않으면 더 많은 메모리를 사용하도록 코드를 조정해야 합니다.폼을 닫고 개발 환경으로 돌아가서 for 루프의 반복 횟수를 10000으로 늘립니다.그런 다음 프로젝트를 다시 실행합니다.
3단계를 반복합니다.이번에는 메모리 관리자에서 더 많은 개체를 종결하므로 시간이 더 오래 걸립니다.
사실 3단계를 반복할 때마다 메모리 관리자에서 단계를 실행하기 전에 CDemo 개체를 더 많이 할당할 수 있을 것입니다.이것은 Visual Studio가 더 많이 대체되어 CDemo 인스턴스에서 사용할 수 있는 공간을 더 많이 남겨 두기 때문입니다.
폼을 닫고 개발 환경으로 돌아갑니다.