수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1694365
  • Today | 110
  • Yesterday | 606

28 Articles, Search for 'Programming/C#'

  1. 2007/07/27 Introduction to Objects and Classes in C#
  2. 2007/07/27 Creational Patterns in C#
  3. 2007/07/27 Exception Handling in C#
  4. 2007/07/02 C# 키워드 목록
  5. 2007/06/26 Event Handling in .NET Using C#
  6. 2007/05/03 C# 명령줄 컴파일러 옵션
  7. 2007/02/05 C# 스레드 사용 (1)
  8. 2007/02/05 Office 2003 : Visual Studio Tools for Office System을 활용한 Excel 게임
  9. 2007/02/05 웹 서비스의 세계로 - 구글 검색을 활용해보자.
  10. 2007/02/05 정렬된 Named Color List 만들기
«Prev  1 2 3  Next»
Programming/C#2007/07/27 09:27

Introduction to Objects and Classes in C#

출처 : http://www.devarticles.com/c/a/c-sharp/ ··· sharp%2F

Introduction to Objects and Classes in C#
(Page 1 of 9 )

In this article Michael introduces us to C#, as well as attempts to demystify the theory behind "Object and Classes" in OO Programming.

In this article I will explain some of the concepts behind object-oriented programming in C#, including objects and classes. To read this article you should have some basic understanding of the C-Sharp language.

Read the whole article because there are some concepts you may not fully understand until you finish the article. And we will revisit all the concepts more than once when I see it's appropriate in future articles; so don't worry at all.

Introduction to Objects and Classes in C# - Introduction
(Page 2 of 9 )

OOP stands for Object-Oriented Programming. OOP is relatively a new way to program computer applications. In the past, OOP programmers used to create computer applications using procedural-programming (or structured-programming). But, when OOP solved a lot of the problems of the procedural-programming, most programmers and developers began using OOP languages. In procedural- programming all the program functionality is written in a few modules of code or maybe one module (depending on the program). These modules depend on one another and sometimes changing a line of code requires you to rewrite the whole module again and maybe the whole program. 

In Object-Oriented Programming programmers write independent parts of a program called classes. Each class represents a part of the program functionality and these classes can be assembled to form a program. When you need to change some of the program functionality all you have to do is to replace the target class which may contain the problem that needs change. So, OOP applications are created by the use of classes and these applications can contain any number of classes. This will get us to discuss the Class and Object concept.


Classes and objects

You may find it a little difficult to understand the class and object story; but, I will try to do my best in explaining it. Actually the class and object concept is related to each other. Some beginners don't care about understanding it clearly so I think they will have a hard time learning C#.

Object-Oriented concepts take most of their functionality from the real-life concepts. For example, I will discuss the concept of Classes and Objects of the world first and then you will understand the computer's Classes and Objects before I even write anything about it.

Introduction to Objects and Classes in C# - World's Classes and Objects
(Page 3 of 9 )

In our world we have classes and objects for those classes. Everything in our world is considered to be an object. For example, people are objects, animals are objects too, minerals are objects; everything in the world is an object. Easy, right? But what about classes?

In our world we have to differentiate between objects that we are living with. So we must understand that there are classifications (this is how they get the name and the concepts of the Class) for all of those objects. For example, I'm an object, David is object too, Maria is another object. So we are from a people class (or type). I have a dog called Ricky so it's an object. My friend's dog, Doby, is also an object so they are from a Dogs class (or type).

A third example: I have a Pentium 3; this is an object.  My friend has a Pentium 4, so this is another object and they are from a Computers class (or type). Now I think you understand the concept of the Class and Object, but let me crystallize it for you. In our world we have classifications for objects and every object must be from some classification. So, a Class is a way for describing some properties and functionalities or behaviors of a group of objects. In other words, the class is considered to be a template for some objects. So maybe I will create a class called person which is a template of the functionality and the properties of persons.

Introduction to Objects and Classes in C# - Programmer’s Classes and Objects
(Page 4 of 9 )

A C# Class is considered to be the primary building block of the language. What I mean by the primary building block is that every time you work with C# you will create classes to form a program. We use classes as a template to put the properties and functionalities or behaviors in one building block for a group of objects and after that we use the template to create the objects we need.

For example, we need to have persons objects in our program so the first thing to do here is to create a class called Person that contains all the functionalities or behaviors and properties of any person and after that we will use that class (or template) to create as many objects as we need. Creating an object of a specific class type is called "an instance of the class". Don't worry if you didn't grasp it 100% and don't worry if you don't know what the class and object's properties and functionalities or behaviors are because we are still in the beginning.  Until now I haven’t provided any code examples. So let's take a brief of what is a class and what is an object:

The class: A building block that contains the properties and functionalities that describe some group of objects. We can create a class Person that contains:

  1. The properties of any normal person on the earth like: hair color, age, height, weight, eye color.
  2. The functionalities or behaviors of any normal person on the earth like: drink water, eat, go to the work.

Later we will see how we can implement the functionalities or behaviors and properties.

There are 2 kinds of classes: The built-it classes that come with the .NET Framework, called Framework Class Library, and the programmer defined-classes which we create ourselves.

The class contains data (in the form of variables and properties) and behaviors (in the form of methods to process these data). We will understand this concept later on in the article.

When we declare a variable in a class we call it member variables or instance variables. The name instance come from the fact that when we create an object we instantiate a class to create that object.  So instance of a class means an object of that class and instance variable means variable that exists in that class.

The object: It's an object of some classification (or class, or type) and when you create the object you can specify the properties of that object. What I mean here is: I, as an object, can have different properties (hair color, age, height, weight) than you as another object. For example, I have brown eyes and you have green eyes.  When I create 2 objects I will specify a brown color for my object's eye color property and I will specify a green color for your object's eye color property.

So to complete my introduction to classes we must discuss properties and variables.

Introduction to Objects and Classes in C# - Properties and Variables
(Page 5 of 9 )

Variables declared in a class store the data for each instance.  What does this mean? It means that when you instantiate this class (that is, when you create an object of this class) the object will allocate memory locations to store the data of its variables. Let's take an example to understand it well.

class Person
{
   public int Age;
   public string HairColor;
}

This is our simple class which contains 2 variables. Don't worry about public keyword now because we will talk about it later. Now we will instantiate this class (that is, when you create an object of this class).

static void Main(string[] args)
{
   Person Michael = new Person();
   Person Mary = new Person();

   // Specify some values for the instance variables
   Michael.Age = 20;
   Michael.HairColor = "Brown";
   Mary.Age = 25;
   Mary.HairColor = "Black";
   // print the console's screen some of the variable's values
   Console.WriteLine("Michael's age = {0}, and Mary's age = {1}",Michael.Age,
       Mary.Age);
   Console.ReadLine();
}

So we begin our Main method by creating 2 objects of type Person. After creating the 2 objects we initialize the instance variables for object Michael and then for object Mary. Finally we print some values to the console.  Here, when you create the Michael object, the C# compiler allocates a memory location for the 2 instance variables to put the values there. Also, the same thing with the Mary object; the compiler will create 2 variables in memory for Mary object. So each object now contains different data. Note that we directly accessed the variables and we put any values we wanted, right?  But wait there is a solution to this problem. We will use properties.

Introduction to Objects and Classes in C# - Properties
(Page 6 of 9 )

Properties are a way to access the variables of the class in a secure manner. Let's see the same example using properties.

class Person
{
   private int age;
   private string hairColor;
   public int Age
   {
       get
       {
       return age;
       }
       set
       {
           if(value <= 65 && value >= 18)
           {
               age = value;
           }
           else
               age = 18;
       }
   }
   public string HairColor
   {
       get
       {
           return hairColor;
       }
       set
       {
           hairColor = value;
       }
   }
}

I made some modifications, but focus on the new 2 properties that I created. So the property consists of 2 accessors. The get accessor, responsible of retrieving the variable value, and the set accessor, responsible of modifying the variable's value. So the get accessor code is very simple. We just use the keyword return with the variable name to return its value. So the following code:

get
  {
   return hairColor;
  }

returns the value stored in hairColor.

[Note]

The keyword value is a reserved keyword by C# (that is, reserved keywords means that these keywords are owned only by C# and you can't create it for any other purposes. For example, you can't create a variable called value .If you did, the C# compiler would generate an error. To make things easier, Visual Studio.NET will color the reserved keywords in blue.)

[/Note]

Let's put this code to work and then discuss the set accessor..

Introduction to Objects and Classes in C# - Reworked
(Page 7 of 9 )

static void Main(string[] args)
{
   Person Michael = new Person();
   Person Mary = new Person();

   // Specify some values for the instance variables
   Michael.Age = 20;
   Michael.HairColor = "Brown";
   Mary.Age = 25;
   Mary.HairColor = "Black";

   // print the console's screen some of the variable's values
   Console.WriteLine("Michael's age = {0}, and Mary's age = {1}",Michael.Age,
       Mary.Age);
   Console.ReadLine();
}

Here I created the same objects from the last example, except that I used only properties to access the variable instead of accessing it directly. Look at the following line of code

Michael.Age = 20;

When you assign a value to the property like that C# will use the set accessor. The great thing with the set accessor is that we can control the assigned value and test it; and maybe change to in some cases. When you assign a value to a property C# changes the value in a variable and you can access the variable's value using the reserved keyword value exactly as I did in the example. Let's see it again here.

set
  {
      if(value <= 65 && value >= 18)
      {
          age = value;
      }
      else
          age = 18;
  }

Here in the code I used if statement to test the assigned value because for some reason I want any object of type Person to be aged between 18 and 65. Here I test the value and if it is in the range then I will simply store it in the variable age. If it's not in the range I will put 18 as a value to age.

Introduction to Objects and Classes in C# - Creating Objects and Classes
(Page 8 of 9 )

We create a class by defining it using the keyword class followed by the class name:

class Person

Then we open a left brace "{" and write our methods and properties.  We then close it with a right brace "}". That's how we create a class. Let's see how we create an instance of that class.

In the same way as we declare a variable of type int we create an object variable of Person type with some modifications:

int age;
Person Michael = new Person();

In the first line of code we specified integer variable called age. In the second line we first specified the type of Object we need to create, followed by the object's name, followed by a reserved operator called new.  We end by typing the class name again followed by parentheses "()".

Let's understand it step-by-step. Specifying the class name at the beginning tells the C# Compiler to allocate a memory location for that type (the C# compiler knows all the variables and properties and methods of the class so it will allocate the right amount of memory). Then we followed the class name by our object variable name that we want it to go by. The rest of the code "= new Person();" calls the object's constructor. We will talk about constructors later but for now understand that the constructor is a way to initialize your object's variable while you are. For example, the Michael object we created in the last section can be written as following :

Person Michael = new Person(20, "Brown");

Here I specified the variable's values in the parameter list so I initialized the variables while creating the object. But for this code to work we will need to specify the constructor in the Person class -- I will not do that yet as constructors will come in a later article.

Introduction to Objects and Classes in C# - Conclusion
(Page 9 of 9 )

In this article, I gave you a concise introduction to classes and objects. I will complete the discussion in my next article, as well as discuss constructors and building block scope. I hope you’ve learned something new thing from this first article.

Introduction to Objects and Classes in C#, Part 2
(Page 1 of 4 )

After I wrote the article named Introduction to Objects and Classes in C#, I got a lot of e-mail messages asking me to create a series of articles about Objects and Classes. Actually this was a few months back (sorry for being late), but I'm here again with part two. In Part one, I explained the concepts behinds Objects and Classes but I didn't discuss why Object Oriented Programming (OOP) uses the Object and Class technique. Today, I will discuss the advantage of Objects and Classes with more details on how to understand your problems and develop your Objects for your solution.

Because this series targets the true beginners, I will not use any technical expressions and I will prefer to explain concepts by examples. I presume that readers have a basic knowledge of C# (control the flow of the program, using methods and arrays, namespaces & assemblies).

The first thing that you should know about C# programming is that it uses the Class to include the data (in part one, I said that data can be stored in instance variables in the class) and methods to process that data. Think about it in the next example:

class Test
{
   
public int Add(int x, int y)
   
{
       
return x + y;
   
}
}

The class Test contains a method called Add (which add to integers and return the result) is a good example for what we are talking about. I said that the Class includes data and there are methods to process that data, here the class test includes the method Add() which takes two integer numbers to add them and this is the functionality of the method. The application that will use the Test class can be something like the following:


public

class Class1
{
   
public Class1()
   
{
       Test t1
= new Test();
       Console
.WriteLine(t1.Add(5,4));
   
}
}

The concept of Objects & Classes helps you hide your code implementation from the user of your class. In other words, if you develop a class for your friend to use, he doesn't have to know how you created the methods of that class in order to use it; he'll just need to know how to use it, and what functionality is offered by your class members. The point here is that you don't have to know how another developer developed a certain class; you just have to know how to interface with it. And I said in the first part, when you develop programs with C# you will develop classes. Note that this is not like C programming, where the programming primary building block was the function (or methods, in C#). Finally, remember that C# defines other types like structures, enumerations. We'll learn about these in future articles.

In C programming language, programmers develop functions to form their applications. These functions contain the code of the program. The problem is that in large programs, if you have to modify just one line of code you may have to modify many functions to fit in the new modifications. (In procedural languages, functions depend on each other.) But in C#, we write classes as our primary building blocks, and because classes hide their code implementation -- and only the methods of these classes are accessible to the applications that use them -- if we must change something inside the class, we will do it without changing the code of the applications that uses our classes.

Introduction to Objects and Classes in C#, Part 2 - Comments
(Page 2 of 4 )

When you write applications in C# try to use comments to describe exactly how your application performs and why; try to make your application easy to understand and easy to maintain. Think about it this way: if you were to come back to your application five years after you wrote it, would you know what every line of code meant and did? What if someone was developing an application and they were trying to read through your code? Without documentation, this is difficult even for the most experienced programmers.  

I think that you want to know more about classes so let's write a class and then discuss  new concepts.

The Person Class

Here's a simple class called Person


class Person

{

   
// These are 3 private instance variables
   // for now just consider them instance members of
   // that class
   private string firstName;
   private string lastName;
   private int age;

   // This called the default constructor
   public Person()
   {

       
firstName = "Unknown";
       lastName
= "Unknown";
       age
= 0;

   }
   
   
// This also a Constructor and we will understand
   // the use of it and why we create constructors
   public Person(string fName, string lName, int pAge)
   {
       firstName = fName;
       lastName = lName;
       age = pAge;
   }

// This is a method just writes a string to the console.
// this string consists of the 3 variables and
// displaying the information about the person.
public void PersonInfo()
{

Console.WriteLine("First Name = {0}, Last Name = {1} and his age = {2}",
firstName
, lastName, age);

}

}

Let's break this down:

You're probably curious about private string firstName, private string lastName, and private int age. These are called instance variables, and as you know from the first part of that article, when you create objects from a given class (for example, the person class) you create an instance of that class. That's why we call the variables declared the body of the class (but not inside any method of that class) an instance variable. When you create an objects of that class C# compiler will allocate separate memory locations for the instance variables of each object. Thus, the object named Michael (of type Person) will contain its own values for these instance members, as will the object Prakhar (of type Person). You can say that the consumer application (the application that will use your class) will provide the values of these instance variables for each object created of that type.

NOTE About the keywords private and public in the class, they called access modifier keywords. When developing C# class you will use the access modifier keywords to specify the scope of the member. Each instance variable, method, or any other type you create inside a class called a member. C# gives you the power to specify the scope of the member using these access modifiers.

Introduction to Objects and Classes in C#, Part 2 - What's a Scope?
(Page 3 of 4 )

Simply, the scope of a type (a variable, a method, or a class) is where you can use that type in your program. In other words, the scope defines the area of the program where that type can be accessible and referenced.

When you declare a variable inside a block of code (like a method or an if statement structure), it will have a local scope, and it will be called a local-variable. Local scope means that you can't refer to that variable outside that block of code. Consider the next example.


class Test

{
   
public void Test1()
   
{
       int x 
= 0;
           
// some code goes here that uses the x variable
   }
   
   
public void Test2()
   
{
       Console
.WriteLine(x);
   
}
}


Try to instantiate this class and you will get a compile-time error inside method Test2() telling you that the name x doesn't exist and that's because x is a local variable to the method Test1() and method Test2() doesn't know anything about it. So x has a local score to method Test1() only. Consider the next example.


class Test

{

   public
void Test1()
   
{
       int x 
= 0;

       if(
x == 0)
       
{

           
Console.WriteLine("x equal to 0");
       
}
   
}
}

Here, the method Test1() declares a local variable x (now x has a local-scope to the method). Try to instance this class and compile the code. It will work! Some beginners think that because I said that x has a local scope to method Test1() it will not be referenced from nested block (like the one we have here, the If statement) but that's not true because any nested block inside Test1() method can refer x because x is local for the method and its all blocks of code.

NOTE  There are 2 kinds of scopes: block scope (the one that we just finished), and a class scope (which we will talk about later in this article). Now, about the keywords private and public in the class person, you can use these access modifier keywords to define the scope of your variables, methods, or even your classes. There are other access modifiers but I will talk about them in a later article when I will explain the concepts of inheritance.

Instance variables declared using the access modifier keyword private will be accessible to the methods between the opening left brace "{" and the closing right brace "}" (which define the body of the class) only. In other words, when you declare an instance variable like in the following example:


public

class Class2
{
   
private int x;
}

public class
Class3
{
   void testing
()
   
{
       x
== 100;
   
}
}

In this example, you will get a compile-time error telling you that you that the name X doesn't exist inside the Class3. Using the keyword private, you explicitly tell the compiler "Don't show this member to any other class." (So you are hiding it inside the class.)

Introduction to Objects and Classes in C#, Part 2 - Private Members Only?
(Page 4 of 4 )

What about objects of that class? Can they access the members declared as private?

In short, no, the objects of this class will not see the private member, but it will have it's own copy because as we said before the class is just a template for the contents of its object. I prefer to discuss it using an example:


public

class Class2
{
   
private int x = 100;
}

And then I will instance 2 objects of that class inside the Main method


static void Main

(string[] args)
{
   Class2 c2
= new Class2();
   Class2 c3
= new Class2();
   
// Now Let's check if we can see
   // x inside any of these objects
   c2.
}

c2 and c3 are objects of type Class2() but look what happened when I typed the "." operator (which will get us all the accessible members of that class).

C#

There is no x here because it's private to the class. You may wonder why this feature exists. Sometimes you need to hide some values inside the class (ie. you don't want other classes or objects of that class to see these values), maybe because it's complex information, or it's private to your work, or the developers that will use your class (after it's compiled) will simply not benefit if they saw these variables or methods.

You can use the access modifier keyword public while you declare your variables to specify that you want your variable to be accessed by the other classes or any objects of this class. let's revise the the x variable in Class2 and check if we can see it or not (from objects of this class or other classes).


public

class Class2
{
   
public int x = 100;
}

Now let's see the result:

C#

Oh, yes we can see the x variable now because we made it public. And I will talk about Class Scope in a later article. For now, just play around with scopes. I think you'll have a lot of fun with these.

"C#" 카테고리의 다른 글
  • Introduction to Objects and Classes in C# (0)2007/07/27
  • Creational Patterns in C# (0)2007/07/27
  • Exception Handling in C# (0)2007/07/27
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
2007/07/27 09:27 2007/07/27 09:27
Posted by webdizen
Tags C#, Classes, Objects
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3100

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/07/27 09:18

Creational Patterns in C#

출처 : http://www.devarticles.com/c/a/c-sharp/ ··· sharp%2F

Creational Patterns in C#
(Page 1 of 6 )

When it comes to asking questions about creating patterns with C#, Rajesh has all the answers. Read about some C# patterns in this article.

The software design patterns are mainly classified into three categories, namely Creational Patterns, Structural Patterns and Behavioral Patterns. The Creational Patterns deals with the best way to create objects. The Singleton Pattern is an example of Creational Pattern.

The singleton design pattern is used when only one instance of an object is needed throughout the lifetime of an application. The singleton class is instantiated at the time of first access and the same instance is used thereafter till the application quits.

The famous GOF defined the Singleton Pattern as follows.

“Ensure a class has only one instance, and provide a global point of access to it.” -- "Design Patterns” Gamma et al., Addison-Wesley, ISBN:0-201-63361-2”

The Singleton class can be used in various places where one would need a common repository of information that can be accessed from all objects in an application. For example sometimes we may need a single Database connection object or Network connection object.

Non-software Example

The office of the President of the United States is a Singleton. The United States Constitution specifies the means by which a president is elected, limits the term of office, and defines the order of succession. As a result, there can be at most one active president at any given time. Regardless of the personal identity of the active president, the title, "The President of the United States" is a global point of access that identifies the person in the office. [Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

It is pretty easy to implement the Singleton Pattern in C#. There are lots trivial ways to achieve this. But by using a private constructor and a static method to create an instance of the class is a popular way to create singleton pattern.

The above program will display OK and then followed by NO MORE OBJECTS. The value of the object sic2 is null, because we can’t create two or more instances of the class SingleInstanceClass.

C# Implementation

//Creational Pattern: SINGLETON
//Implemenation in C#
//By
rajeshvs@msn.com
/*The constructor should be private. Provide a static method, which returns an instance of the class. use a static variable to check whether already one instance is created or not. if already an instance is there , returns a null */
using System;
class SingleInstanceClass
{
private static SingleInstanceClass sic= null;
private static bool instanceFlag = false;

private SingleInstanceClass()
{
}
public static SingleInstanceClass Create()
{
 if(! instanceFlag)
 {
  sic = new SingleInstanceClass();
  instanceFlag = true;
  return sic;
 }
 else
 {
  return null;
 }
}
protected void Finalize()
{
 instanceFlag = false;
}
}
class MyClient
{
public static void Main()
{
 SingleInstanceClass sic1,sic2;
 sic1 = SingleInstanceClass.Create();
 if(sic1 != null)
  Console.WriteLine("OK");
 sic2 = SingleInstanceClass.Create();
 if(sic2 == null)
  Console.WriteLine("NO MORE OBJECTS");
}
}

The above program returns a null value when try to create an object second time. But instead of returning null, it is possible to return already existing object ‘sic’ by changing ‘return null’ to ‘return sic’ in the above program.

Creational Patterns in C# - The Factory Method Pattern
(Page 2 of 6 )

The Factory Method Pattern comes under the classification of Creational Patterns. The creational patterns deals with the best way to create objects. The Factory Method provides a simple decision making class that can return the object of one of several subclasses of an abstract base class depending on the information that are provided.

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.” -- "Design Patterns” Gamma et al., Addison-Wesley, ISBN:0-201-63361-2”

Non-software Example

Injection molding presses demonstrate this pattern. Manufacturers of plastic toys process plastic molding powder, and inject the plastic into molds of the desired shapes. The class of toy (car, action figure, etc.) is determined by the mold. [Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

A factory pattern is one that returns an instance of one of several possible classes depending on the data provided to it. Usually all of the classes it returns should have a common base class and common methods, but implementations of the methods may be different.

The following is an UML representation of the Factory Method Pattern. In this case we are not directly creating an instance of the class Derived1 and Derived2. Instead we are using the getObject() method of the Factory class to return an appropriate instance depending on the value passed to the getObject() method. This method is commonly knows as the Factory method and Factory method can be either static or non-static in nature.

C# Implementation

//Creational Pattern: The Factory Method
//Author:
rajeshvs@msn.com
/* In Factory method pattern, A Factory class contains a factory method is used for creating the object. This factory method can be either static or non-static. */
using System;
class Factory
{
public Base GetObject(int type)
{
 Base base1 = null;
 switch(type)
 {
  case 1:
    base1 = new Derived1();
    break;
  case 2:
    base1 = new Derived2();
    break;
 }
 return base1;
}
}
interface Base
{
  void DoIt();
}
class Derived1 : Base
{
   public void DoIt()
   {
 Console.WriteLine("Derived 1 method");
   }
}
class Derived2 : Base
{
   public void DoIt()
   {
 Console.WriteLine("Derived 2 method");
   }
}
//Client class
//Client class needn’t know about instance creation. The creation of Product is //deferred to the Factory class
class MyClient
{
   public static void Main()
   {
     Factory factory = new Factory();//Decides which object must create.
     Base obj = factory.GetObject(2);
     obj.DoIt();
   }
}

This is what the fundamental principle of Factory pattern. We create an abstraction, which decides which of several possible classes to return, and returns one. After that we can call the methods of that class instance without ever knowing which derived class is using actually. The object creation happens in a single place that is inside the Factory class.

Remember that the Factory class can contain more than one Factory methods. Even these Factory methods can be either static or non-static.

Creational Patterns in C# - The Abstract Factory Pattern
(Page 3 of 6 )

The Abstract Factory Pattern comes under the classification of Creational Patterns. The creational patterns deals with the best way to create objects. The Abstract Factory provides an interface to create and return one of several families of related objects.

“Provide an interface for creating families of related or dependent objects without specifying their concrete classes” -- "Design Patterns” Gamma et al., Addison-Wesley, ISBN:0-201-63361-2”

Non-software Example

This pattern is found in the sheet metal stamping equipment used in the manufacture of Japanese automobiles. The stamping equipment is an Abstract Factory, which creates auto body parts. The same machinery is used to stamp right hand doors, left hand doors, right front fenders, left front fenders, hoods, etc. for different models of cars. Through the use of rollers to change the stamping dies, the concrete classes produced by the machinery can be changed within three minutes.

[Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

The abstract factory is a factory object that returns one of several factories. It can be used to return one of several related classes of objects, each of which can return several different objects on request.

The abstract factory pattern can be interpreted and implemented in many ways. The following is a simples interpretation and implementation of this pattern.

In this case the interface Factory has two concrete implementations, ConcreteFactory1 and ConcreteFactory2. The getObject() inside these concrete classes returns Derived1 and Derived2 objects respectively. The client can decide which ConcreteFactory class has to be used during the run-times.

The following is a more complicated interpretation and implementation of this pattern. Here those Factory class methods are used for returning objects of two different class hierarchies.

C# Implementation

// Creational Pattern: Abstract Factory Pattern
//Author:
rajeshvs@msn.com
/*
In the following snippet, Factory is an interface. The concrete implementation of this
interface ConcreteFactory1 and ConcreteFactory2 implements the method getObject so
that it returns Derived1 and Derived2 objects respectively. The Base is an interface and
Derived1 and Derived2 are the concrete implementations of the base class. The client
(MyClient class) always uses the Factory implementations to create an instance of the
Base classes. Actually the derived classes of Factory interface decided which object
(either Derived1 or Derived2) has to be created.
*/
using System;
interface Factory
{
  Base GetObject();
}
//This class is responsible for creating objects of the class Derived1.
class ConcreteFactory1 :Factory
{
  public Base GetObject()
  {
return new Derived1();  
  }
}
//This class is responsible for creating objects of the class Derived2.
class ConcreteFactory2 : Factory
{
  public Base GetObject()
  {
return new Derived2();  
  }
}
interface Base
{
  void DoIt();
}
class Derived1 : Base
{
  public void DoIt()
  {
Console.WriteLine("Derived 1 method");
  }
}
class Derived2 : Base
{
  public void DoIt()
  {
Console.WriteLine("Derived 2 method");
  }
}
/*
Client class Client class needn’t know about instance creation. The creation of Product
is  deferred to he ConcreteFactory1.
*/
class MyClient
{
  public static void Main()
  {
   Factory factory = new ConcreteFactory2();//Decides which object must create.
   Base obj = factory.GetObject();
   obj.DoIt();
  }
}

Creational Patterns in C# - The Builder Pattern
(Page 4 of 6 )

The Builder Pattern comes under the classification of Creational Patterns. The creational patterns deals with the best way to create objects. The Builder Pattern separates the construction of a complex object from its representation so that several different representations can be created depending on the needs of the program.

“Separate the construction of a complex object from its representation so that the same construction process can create different representations.” ” -- "Design Patterns” Gamma et al., Addison-Wesley, ISBN:0-201-63361-2”

Builder is an object creational design pattern that codifies the construction process outside of the actual steps that carries out the construction - thus allowing the construction process itself to be reused.

Non-software Example

Fast food restaurants to construct children’s meals use this pattern. Children's meals typically consist of a main item, a side item, a drink, and a toy (e.g., a hamburger, fries, Coke, and toy car). Note that there can be variation in the content of the children's meal, but the construction process is the same. Whether a customer orders a hamburger, cheeseburger, or chicken, the process is the same.

The employee at the counter directs the crew to assemble a main item, side item, and toy. These items are then placed in a bag. The drink is placed in a cup and remains outside of the bag. This same process is used at competing restaurants. [Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

Another example for Builder pattern is a Computer Assembly. A computer is nothing but the bundling of various components like FDD, HDD, Monitor etc. But when an user buys a computer someone assemble all these components and given to us. Remember that here the building process is completely hidden from the client or user.

The UML diagram for a Builder pattern is more or less like following one.

Remember that a project can contain one or more builders and each builder is independent of others. This will improves the modularity and makes the addition of other builders relatively simple. Since each builder constructs the final product step by step, we have more control over the final product that a builder constructs.

C# Implementation

//Creational Pattern: BUILDER
//Author: rajeshvs@msn.com
using System;
class Director
{
public void Construct(IBuilder builder)
{
 builder.DoIt();
}
}
interface IBuilder
{
void DoIt();
}
class BuilderA : IBuilder
{
public void DoIt()
{
 //Necessary code for building the computer type A
 Console.WriteLine("Assembly a Computer with mono monitor");
}
}
class BuilderB : IBuilder
{
public void DoIt()
{
 //Necessary code for building the computer type B
 Console.WriteLine("Assembly a Computer with color monitor");
}
}
class MyClient
{
public static void Main()
{
 Director d = new Director();
 IBuilder build = new BuilderA();
 d.Construct(build);
}
}

Creational Patterns in C# - The Prototype Pattern
(Page 5 of 6 )

The Prototype Pattern comes under the classification of Creational Patterns. The creational patterns deals with the best way to create objects. This helps to copy or clone the existing objects to create new ones rather than creating from the scratch.

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. -- "Design Patterns” Gamma et al., Addison-Wesley, ISBN:0-201-63361-2”

The prototype pattern is used when creating an instance of a class is very time consuming or complex in some way. Then rather than creating more instances, it is possible to make copies of the original instances and modifying them as appropriate.

When we are not in a position to call a constructor for an object directly, we could alternatively clone a pre-existing object  (a prototype) of the same class. When there are many subclasses that differ only in the kind of objects they create a Prototype Pattern can be used to reduce the number of subclasses by cloning a prototype. Prototype Design Pattern helps in reducing number of classes.

For example suppose we have to do say Sales Analysis on a set of data in the database. Normally we will create an object encapsulating this data and do the Sales Analysis. Suppose now we have to do another type of analysis say Promotion Analysis on the same data. Now instead of creating another object corresponds to the data from the scratch, we can clone the existing object and do the analysis. This is one of the classical use of prototype pattern.

Remember that in C#, this pattern can be implemented easily by using the clone(). Any class, which wants to support cloning, should inherit from the ICloneable interface in C#. ICloneable interface contains a Clone() method which we can override in our class. Clone can be implemented either as a deep copy or a shallow copy. In a deep copy, all objects are duplicated; whereas, in a shallow copy, only the top-level objects are duplicated and the lower levels contain references.

The resulting clone must be of the same type as or a compatible type to the original instance.

Creational Patterns in C# - Summary
(Page 6 of 6 )

The Singleton Pattern is a pattern that insures there are one and only one instance of an object, and that it is possible to obtain global access to that one instance.

The Factory Pattern is used to choose and return an instance of a class from a number of similar classes based on data you provide to the factory.

The Abstract Factory Pattern is used to return one of several groups of classes. In some cases it actually returns a Factory for that group of classes.

The Builder Pattern assembles a number of objects to make a new object, based on the data with which it is presented. Frequently, the choice of which way the objects are assembled is achieved using a Factory.

The Prototype Pattern copies or clones an existing class rather than creating a new instance when creating new instances is more expensive.

"C#" 카테고리의 다른 글
  • Introduction to Objects and Classes in C# (0)2007/07/27
  • Creational Patterns in C# (0)2007/07/27
  • Exception Handling in C# (0)2007/07/27
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
2007/07/27 09:18 2007/07/27 09:18
Posted by webdizen
Tags C#, Pattern
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3098

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/07/27 09:14

Exception Handling in C#

As we all know, exception handling becomes a very handy tool for debugging an application. Rajesh will now explein how one should use Exceptions in C#.

Exception handling is an in built mechanism in .NET framework to detect and handle run time errors. The .NET framework contains lots of standard exceptions. The exceptions are anomalies that occur during the execution of a program. They can be because of user, logic or system errors. If a user (programmer) do not provide a mechanism to handle these anomalies, the .NET run time environment provide a default mechanism, which terminates the program execution. 

C# provides three keywords try, catch and finally to do exception handling. The try encloses the statements that might throw an exception whereas catch handles an exception if one exists. The finally can be used for doing any clean up process.

The general form try-catch-finally in C# is shown below 

try
{
// Statement which can cause an exception.
}
catch(Type x)
{
// Statements for handling the exception
}
finally
{
//Any cleanup code
}

If any exception occurs inside the try block, the control transfers to the appropriate catch block and later to the finally block.  

But in C#, both catch and finally blocks are optional. The try block can exist either with one or more catch blocks or a finally block or with both catch and finally blocks. 

If there is no exception occurred inside the try block, the control directly transfers to finally block. We can say that the statements inside the finally block is executed always. Note that it is an error to transfer control out of a finally block by using break, continue, return or goto. 

In C#, exceptions are nothing but objects of the type Exception. The Exception is the ultimate base class for any exceptions in C#. The C# itself provides couple of standard exceptions. Or even the user can create their own exception classes, provided that this should inherit from either Exception class or one of the standard derived classes of Exception class like DivideByZeroExcpetion ot ArgumentException etc. 

Uncaught Exceptions

The following program will compile but will show an error during execution. The division by zero is a runtime anomaly and program terminates with an error message. Any uncaught exceptions in the current context propagate to a higher context and looks for an appropriate catch block to handle it. If it can’t find any suitable catch blocks, the default mechanism of the .NET runtime will terminate the execution of the entire program. 

//C#: Exception Handling
//Author:
rajeshvs@msn.com
   
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 100/x;
                       Console.WriteLine(div);
           }
}

The modified form of the above program with exception handling mechanism is as follows. Here we are using the object of the standard exception class DivideByZeroException to handle the exception caused by division by zero. 

//C#: Exception Handling
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine(“This line in not executed”);
                       }
                       catch(DivideByZeroException de)
                       {
                                   Console.WriteLine("Exception occured");
                                  
                      }
                       Console.WriteLine("Result is {0}",div);
           }
}

In the above case the program do not terminate unexpectedly. Instead the program control passes from the point where exception occurred inside the try block to the catch blocks. If it finds any suitable catch block, executes the statements inside that catch and continues with the normal execution of the program statements.

If a finally block is present, the code inside the finally block will get also be executed.  

//C#: Exception Handling
//Author:
rajeshvs@msn.com
  using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine("Not executed line");
                       }
                       catch(DivideByZeroException de)
                       {
                                   Console.WriteLine("Exception occured");
                       }
                       finally
                       {
                                   Console.WriteLine("Finally Block");
                       }
                       Console.WriteLine("Result is {0}",div);
           }
}

Remember that in C#, the catch block is optional. The following program is perfectly legal in C#.

//C#: Exception Handling
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine("Not executed line");
                       }
                       finally
                       {
                                   Console.WriteLine("Finally Block");
                       }
                       Console.WriteLine("Result is {0}",div);
           }
}
 

But in this case, since there is no exception handling catch block, the execution will get terminated. But before the termination of the program statements inside the finally block will get executed. In C#, a try block must be followed by either a catch or finally block 

Multiple Catch Blocks

A try block can throw multiple exceptions, which can handle by using multiple catch blocks. Remember that more specialized catch block should come before a generalized one. Otherwise the compiler will show a compilation error. 

//C#: Exception Handling: Multiple catch
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine("Not executed line");
                       }
                       catch(DivideByZeroException de)
                       {
                                   Console.WriteLine("DivideByZeroException" );
                       }
                       catch(Exception ee)
                       {
                                   Console.WriteLine("Exception" );
                       }
                       finally
                       {
                                   Console.WriteLine("Finally Block");
                       }
                       Console.WriteLine("Result is {0}",div);
           }
}
 

Catching all Exceptions

By providing a catch block without a brackets or arguments, we can catch all exceptions occurred inside a try block. Even we can use a catch block with an Exception type parameter to catch all exceptions happened inside the try block since in C#, all exceptions are directly or indirectly inherited from the Exception class.  

//C#: Exception Handling: Handling all exceptions
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine("Not executed line");
                       }
                       catch
                       {
                                   Console.WriteLine("oException" );
                       }
                       Console.WriteLine("Result is {0}",div);
           }
}
 

The following program handles all exception with Exception object.

//C#: Exception Handling: Handling all exceptions
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       int x = 0;
                       int div = 0;
                       try
                       {
                                   div = 100/x;
                                   Console.WriteLine("Not executed line");
                       }
                       catch(Exception e)
                       {
                                   Console.WriteLine("oException" );
                       }
                       Console.WriteLine("Result is {0}",div);
           }
}

Throwing an Exception

In C#, it is possible to throw an exception programmatically. The ‘throw’ keyword is used for this purpose. The general form of throwing an exception is as follows.

throw exception_obj; 

For example the following statement throw an ArgumentException explicitly.

throw new ArgumentException(“Exception”); 

//C#: Exception Handling:
//Author:
rajeshvs@msn.com
using System;
class MyClient
{
           public static void Main()
           {
                       try
                       {
                                   throw new DivideByZeroException("Invalid Division");
                       }
                       catch(DivideByZeroException e)
                       {
                                   Console.WriteLine("Exception" );
                       }
                       Console.WriteLine("LAST STATEMENT");
           }
}
 

Re-throwing an Exception

The exceptions, which we caught inside a catch block, can re-throw to a higher context by using the keyword throw inside the catch block. The following program shows how to do this.  

//C#: Exception Handling: Handling all exceptions
//Author:
rajeshvs@msn.com
using System;
class MyClass
{
           public void Method()
           {
                       try
                       {
                                   int x = 0;
                                   int sum = 100/x;
                       }
                       catch(DivideByZeroException e)
                       {
                                   throw;
                       }
           }
}
class MyClient
{
           public static void Main()
           {
                       MyClass mc = new MyClass();
                       try
                       {
                                   mc.Method();
                       }
                       catch(Exception e)
                       {
                                   Console.WriteLine("Exception caught here" );
                       }
                       Console.WriteLine("LAST STATEMENT");
           }
}
 

Standard Exceptions
 
There are two types of exceptions: exceptions generated by an executing program and exceptions generated by the common language runtime. System.Exception is the base class for all exceptions in C#. Several exception classes inherit from this class including ApplicationException and SystemException. These two classes form the basis for most other runtime exceptions. Other exceptions that derive directly from System.Exception include IOException, WebException etc. 

The common language runtime throws SystemException. The ApplicationException is thrown by a user program rather than the runtime. The SystemException includes the ExecutionEngineException, StaclOverFlowException etc. It is not recommended that we catch SystemExceptions nor is it good programming practice to throw SystemExceptions in our applications.

System.OutOfMemoryException

System.NullReferenceException

System.InvalidCastException

System.ArrayTypeMismatchException

System.IndexOutOfRangeException        

System.ArithmeticException

System.DevideByZeroException

System.OverFlowException

User-defined Exceptions

In C#, it is possible to create our own exception class. But Exception must be the ultimate base class for all exceptions in C#. So the user-defined exception classes must inherit from either Exception class or one of its standard derived classes.

//C#: Exception Handling: User defined exceptions
//Author:
rajeshvs@msn.com
using System;
class MyException : Exception
{
           public MyException(string str)
           {
                       Console.WriteLine("User defined exception");
           }
}
class MyClient
{
           public static void Main()
           {
                       try
                       {
                                   throw new MyException("RAJESH");
                       }
                       catch(Exception e)
                       {
                                   Console.WriteLine("Exception caught here" + e.ToString());
                       }
                       Console.WriteLine("LAST STATEMENT");
           }
}

Design Guidelines

Exceptions should be used to communicate exceptional conditions. Don’t use them to communicate events that are expected, such as reaching the end of a file. If there’s a good predefined exception in the System namespace that describes the exception condition-one that will make sense to the users of the class-use that one rather than defining a new exception class, and put specific information in the message.

Finally, if code catches an exception that it isn’t going to handle, consider whether it should wrap that exception with additional information before re-throwing it.

"C#" 카테고리의 다른 글
  • Introduction to Objects and Classes in C# (0)2007/07/27
  • Creational Patterns in C# (0)2007/07/27
  • Exception Handling in C# (0)2007/07/27
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
2007/07/27 09:14 2007/07/27 09:14
Posted by webdizen
Tags C#, exception, Handing
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3097

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/07/02 22:09

C# 키워드 목록

출처 : C# and the .NET Platform (Second Edition) - Andrew Troelsen, 장시형 역

C# 키워드 설명
bool, byte, char, float, uint, ulong, ushort, decimal, int, sbyte, short, void, double, long, string, object 이 C# 키워드들은 System 네임스페이스에 있는 구조체들에 대한 별칭으로서, CTS의 핵심 데이터 형식을 나타낸다(서명되지 않은 형식은 CLS를 준수하지 않는다).
null 'null' 키워드는 null 참조를 나타내는 리터럴이다.
true, false 이 키워드들은 System.Boolean 형식에 할당될 수 있는 값들을 나타낸다.
out, ref, params 이 키워드들은 형식 멤버로 전달되는 매개변수를 제어하는 데 이용된다.
public, private, internal, protected 이 키워드들은 형식과 멤버들의 가시성(visibility)을 제어하는 데 이용된다.
class, interface, struct, enum, delegate, event 이 C# 키워드들은 사용자 지정 CTS 형식과 형식 멤버들을 만드는 데 이용된다.
return 이 키워드는 형식 멤버의 반환 값을 지정하는 데 이용된다.
as, is 이 키워드들은 한 형식이 다른 형식과 호환되는지를 런타임에 검사할 때 이용된다.
do, while, foreach, in, for 이 C# 키워드들은 반복 구조를 나타낸다.
if, else, switch, case, default, break 이 C# 키워드들은 반복 구조를 나타낸다.
goto, continue 이 키워드들은 선택 구조와 반복 구조에서 흐름 제어에 이용된다.
try, catch, throw, finally 이 키워드들은 런타임 예외를 처리하는 데 이용된다.
operator, explicit, implicit 이 C# 키워드들은 오버로드된 연산자와 사용자 지정 변환 루틴을 지원하는 형식을 만드는 데 이용된다.
this, base 이 키워드들은 현재 객체나 참조 형식의 기본 클래스를 참조하는 데 이용된다.
abstract, virtual, override 이 C#키워드들은 이용하면 클래스 계층에 다형성을 제공할 수 있다.
namespace 이 키워드는 사용자 지정 형식을 포함하는 사용자 지정 네임스페이스를 정의한다.
using

이 키워드는 두 가지 환경에서 이용될 수 있다.
- 네임스페이스 참조
- 객체 자동 처리(disposal)

new

이 키워드는 두 가지 의미로 사용된다.
- 형식 할당
- 상속된 멤버 shadowing

const 이 키워드를 이용하면 상수(즉, 변경할 수 없는) 데이터 포인트를 생성할 수 있다.
checked, unchecked 이 C# 키워드들은 수치 연산과 변환에 대한 오버플로 검사 컨텍스트를 제어하는 데 이용된다.
unsafe, fixed, stackalloc 이 키워드들은 C#으로 메모리 포인터를 이용한 작업에 필요한 안전하지 않은 컨텍스트를 선언하고 사용하는 데 이용된다.
extern 이 키워드는 어떤 멤버가 외부 C 기반 모듈(PInvoke 연산 동안 사용되는)에 정의되어 있다는 것을 나타내는 데 이용된다.
sealed 이 키워드는 확장될 수 없는 클래스 형식을 만드는 데 이용된다.
sizeof 'sizeof' 연산자는 값 형식의 크기를 바이트로 얻어내는 데 이용된다.
volatile 이 키워드는 확장될 수 없는 클래스 형식을 만드는 데 이용된다.
static 이 키워드는 이 키워드가 정의된 형식의 모든 인스턴스에 의해서 공유되는 멤버(또는 데이터 포인트)를 정의하는 데 이용된다.
lock 이 C# 키워드에 대해서는 다중 스레드 프로그래밍을 살펴보면서 알아보게 될 것인데, 스레드로부터 안전한 코드 블록을 표시하는 데 이용된다.
readonly 이 키워드는 선언하면서 또는 동일 클래스의 생성자에서 값이 할당될 수만 있는 필드를 선언한다.
typeof 이 키워드는 System.Reflection을 살펴볼 때 보게 될 것인데, 이 키워드를 이용하면 이 연산자로 전달된 항목에 대한 메타데이터 기술어(descriptor)가 포함된 System.Type 변수를 얻을 수 있다.
"C#" 카테고리의 다른 글
  • Creational Patterns in C# (0)2007/07/27
  • Exception Handling in C# (0)2007/07/27
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
  • C# 명령줄 컴파일러 옵션 (0)2007/05/03
2007/07/02 22:09 2007/07/02 22:09
Posted by webdizen
Tags C#, Keyword, 키워드
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3060

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/06/26 01:16

Event Handling in .NET Using C#

출처 : http://www.developerfusion.co.uk/show/2137

Introduction

In this article I discuss the event handling model in .NET using C#. The discussion starts with an introduction to the concept of delegates and then it extends that concept to events and event handling in .NET. Finally, I apply these concepts to GUI event handling using windows forms. Complete code is provided in each step of the discussions.

Event handling is familiar to any developer who has programmed graphical user interfaces (GUI). When a user interacts with a GUI control (e.g., clicking a button on a form), one or more methods are executed in response to the above event. Events can also be generated without user interactions. Event handlers are methods in an object that are executed in response to some events occurring in the application. To understand the event handling model of .Net framework, we need to understand the concept of delegate.

Delegates in C#

A delegate in C# allows you to pass methods of one class to objects of other classes that can call those methods. You can pass method m in Class A, wrapped in a delegate, to class B and Class B will be able to call method m in class A. You can pass both static and instance methods. This concept is familiar to C++ developers who have used function pointers to pass functions as parameters to other methods in the same class or in another class. The concept of delegate was introduced in Visulal J++ and then carried over to C#. C# delegates are implemented in .Net framework as a class derived from System.Delegate. Use of delegate involves four steps.

1. Declare a delegate object with a signature that exactly matches the method signature that you are trying to encapsulate.
2. Define all the methods whose signatures match the signature of the delegate object that you have defined in step 1.
3. Create delegate object and plug in the methods that you want to encapsulate.
4. Call the encapsulated methods through the delegate object.

The following C# code shows the above four steps implemented using one delegate and four classes. Your implementation will vary depending on the design of your classes.


Event Handlers in C#

An event handler in C# is a delegate with a special signature, given below.

public delegate void MyEventHandler(object sender, MyEventArgs e);

The first parameter (sender) in the above declaration specifies the object that fired the event. The second parameter (e) of the above declaration holds data that can be used in the event handler. The class MyEventArgs is derived from the class EventArgs. EventArgs is the base class of more specialized classes, like MouseEventArgs, ListChangedEventArgs, etc. For GUI event, you can use objects of these specialized EventArgs classes without creating your own specialized EventArgs classes. However, for non GUI event, you need to create your own specialized EventArgs class to hold your data that you want to pass to the delegate object. You create your specialized EventArgs class by deriving from EventArgs class.

In case of event handler, the delegate object is referenced using the key word event as follows

Now, we will set up two classes to see how this event handling mechanism works in .Net framework. The step 2 in the discussion of delegates requires that we define methods with the exact same signature as that of the delegate declaration. In our example, class A will provide event handlers (methods with the same signature as that of the delegate declaration). It will create the delegate objects (step 3 in the discussion of delegates) and hook up the event handler. Class A will then pass the delegate objects to class B. When an event occurs in Class B, it will execute the event handler method in Class A.



GUI Event Handling

Event handling in Windows Forms (.NET frame work that supports GUI application) employ the .NET event handling model described earlier. We will now apply that model to write a simple application. The application has one class, MyForm, derived from System.Windows.Forms.Form class. Class MyForm is derived from Form class. If you study the code and the three comment lines, you will observe that you do not have to declare the delegates and reference those delegates using event keyword because the events (mouse click, etc.) for the GUI controls (Form, Button, etc.) are already available to you and the delegate is System.EventHandler. However, you still need to define the method, create the delegate object (System.EventHandler) and plug in the method, that you want to fire in response to the event (e.g. a mouse click), into the delegate object.



Conclusion

Other popular object oriented languages like, Java and Smalltalk do not have the concept of delegates. It is new to C# and it derives its root from C++ and J++. I hope that the above discussions will clear this concept to programmers who are starting out C# as their first object oriented language. If you are using Visual Studio IDE for your C# GUI development, attaching your delegate methods to the events generated by GUI controls (like mouse click on a button) can be done without you writing the code. Still, it is better to know what is going on under the hood.

"C#" 카테고리의 다른 글
  • Exception Handling in C# (0)2007/07/27
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
  • C# 명령줄 컴파일러 옵션 (0)2007/05/03
  • C# 스레드 사용 (1)2007/02/05
2007/06/26 01:16 2007/06/26 01:16
Posted by webdizen
Tags C#, Delegate, Event, Handling
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3057

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/05/03 13:26

C# 명령줄 컴파일러 옵션

침고 도서: C# and the .NET Plaform (apress)

csc.exe명령줄 플래그
설명
@ 컴파일하는 동안 사용될 지시 파일을 지정하는 데 사용된다.
/? or /help csc.exe의 모든 명령줄 플레그(즉, 이 테이블에 있는 정보)를 보여준다.
/addmodule 다중 어셈블리를 추가할 모듈들을 지정하는 데 사용한다.
/baseaddress *.dll을 로드할 기본 설정 기준 주소를 지정하는 데 사용된다.
/bugreport 해당 컴파일에 대한 텍스트 기반의 버그 리포트를 작성하는 데 사용된다.
/checked 데이터 형식의 범위를 넘어서는(overflow, 오버플로우) 정수 연산이 있을 경우 이 연산으로 인해 런타임에 예외가 발생하는지 여부를 지정하는 데 사용된다.
/codepage 컴파일할 때 모든 소스 코드 파일에 사용할 코드 페이지를 지정하는 데 사용된다.
/debug csc.exe가 디버깅 정보를 내보내게 한다.
/define 전처리기 기호를 정의하는 데 사용된다.
/doc XML 문서 파일을 구성하는 데 사용된다.
/filealign 출력 파일의 섹션 크기를 지정한다.
/fullpaths 컴파일러 출력 파일에 절대 경로를 지정한다.
/incremental 소스 코드 파일의 증분 컴파일을 가능하게 한다.
/lib /reference를 통해 참조하는 어셈블리의 위치를 지정한다.
/linkresource 관리 리소스에 대한 링크를 생성한다.
/main 해당 *.cs 파일들에 여러 개의 Main() 메소드가 정의되어 있는 경우 프로그램의 진입점으로 사용할 Mani() 메소드를 지정한다.
/nologo 파일을 컴파일할 때 컴파일러 배너 정보를 표시하지 않는다.
/nostdlib 핵심 .NET 라이브러리인 mscorlib.dll을 자동적으로 import 하지 않게 한다.
/noconfig 컴파일하는 동안 csc.rep 파일을 사용하지 않게 한다.
/nowarn 지정된 경고를 생성하는 컴파일러 기능을 비활성화한다.
/optimize 최적화를 사용하거나 사용하지 않는다.
/out 출력 파일의 이름을 지정한다.
/recurse 하위 디렉토리에서 컴파일할 소스 파일을 검색한다.
/reference 외부 어셈블리 참조에 사용된다.
/resource 컴파일 결과 생성되는 어셈블리에 .NET 리소스를 포함시킨다.
/targer 출력 파일의 포멧을 지정한다.
/unsafe C#의 'unsafe' 키워드가 사용된 코드를 컴파일한다.
/uft8output UTF-8 인코딩을 사용하여 컴파일러 출력을 표시한다.
/warn 컴파일 시의 경고 수준을 설정하는 데 사용된다.
/warnaserror 경고 수준을 자동적으로 오류로 올리는 데 사용된다.
/win32icon .ico 파일을 출력 파일에 삽입한다.
/win32res Win32 리소스를 출력 파일에 삽입한다.
"C#" 카테고리의 다른 글
  • C# 키워드 목록 (0)2007/07/02
  • Event Handling in .NET Using C# (0)2007/06/26
  • C# 명령줄 컴파일러 옵션 (0)2007/05/03
  • C# 스레드 사용 (1)2007/02/05
  • Office 2003 : Visual Studio Tools for Office Sy... (0)2007/02/05
2007/05/03 13:26 2007/05/03 13:26
Posted by webdizen
Tags C#, 컴파일러 옵션
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2886

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/02/05 17:52

C# 스레드 사용

고수닷넷 - 방랑자님


Greg Ewing
Clarity Consulting Inc.

요약: 이 기사에서는 스레딩의 다른 모델(단일, 아파트 및 자유)과 각 모델의 사용에 대해 설명합니다. 스레드를 이용하는 응용 프로그램을 작성하는 데 도움을 줄 수 있도록 스레드를 사용하는 C# 코드 샘플도 소개합니다. 또한 다중 스레딩 코드에 포함된 중요한 문제에 대해서도 설명합니다(9페이지/인쇄 페이지 기준).

목차

소개
스레딩에 대한 배경 지식
예제 응용 프로그램
다중 스레드 코드의 문제
결론

소개

다중 스레드 MSMQ(Microsoft Message Queuing) 트리거 응용 프로그램을 작성하는 일은 일반적으로 까다로운 작업이었습니다. 그러나 .NET Framework 스레딩 및 메시징 클래스의 도입으로 어느 때보다 쉬워졌습니다. 이 클래스를 사용하면 .NET Framework를 대상으로 하는 모든 언어로 다중 스레드 응용 프로그램을 작성할 수 있습니다. 이전에 Microsoft Visual Basic과 같은 도구는 스레딩에 대한 지원이 매우 제한되어 있었습니다. 따라서 C++을 사용하여 다중 스레드 코드를 작성하거나 Visual Basic에서 여러 프로세스나 ActiveX DLL로 구성되는 이상적이지 않은 솔루션을 작성하거나 또는 다중 스레딩을 완전히 무시하는 수 밖에 없었습니다. .NET Framework에서는 어떤 언어를 사용하는지에 관계없이 풍부한 다중 스레드 응용 프로그램을 작성할 수 있습니다.

이 기사에서는 Microsoft 메시지 대기열의 메시지를 수신하고 처리하는 다중 스레드 응용 프로그램을 작성하는 프로세스를 단계적으로 소개하며, 특히 System.Threading 및 System.Messaging이라는 두 가지 네임스페이스에 초점을 둡니다. 샘플 코드는 C#으로 작성되어 있지만 원하는 다른 언어로 쉽게 변환할 수 있습니다.

스레딩에 대한 배경 지식

Win32 환경에서 스레딩의 기본 모델은 단일, 아파트 및 자유 등 세 가지입니다.

단일 스레딩

처음에 작성한 응용 프로그램은 아마 응용 프로그램의 프로세스에 해당하는 스레드만 포함된 단일 스레드였을 것입니다. 프로세스는 해당 응용 프로그램의 메모리 공간을 차지하는 응용 프로그램의 인스턴스로 정의할 수 있습니다. 대부분의 Windows 응용 프로그램은 단일 스레드에서 모든 작업을 수행하는 단일 스레드 응용 프로그램입니다.

아파트 스레딩

아파트 스레딩은 단일 스레드보다 복잡한 스레딩 모델입니다. 아파트 스레딩으로 표시된 코드는 자체의 아파트로 제한된 고유 스레드에서 실행될 수 있습니다. 스레드는 처리 시간 동안 일어날 프로세스에서 소유하는 엔터티로 정의할 수 있습니다. 아파트 스레딩 모델에서 모든 스레드는 기본 응용 프로그램의 메모리에서 각각의 하위 섹션 내에서만 작동합니다. 이 모델에서는 코드의 여러 인스턴스를 동시에 그리고 독립적으로 실행할 수 있습니다. 예를 들어 .NET 이전의 Visual Basic에서는 아파트 스레드 구성 요소와 응용 프로그램을 만드는 것으로 제한되어 있었습니다.

자유 스레딩

자유 스레딩은 가장 복잡한 스레딩 모델입니다. 자유 스레딩 모델에서는 동시에 여러 스레드가 같은 메서드와 구성 요소로 호출됩니다. 아파트 스레딩과 달리 자유 스레딩은 분리된 메모리 공간에 제한되지 않습니다. 예를 들어 응용 프로그램에서 매우 비슷하지만 독립적인 수학 계산을 대량으로 실행해야 하는 경우에 자유 스레드 개체를 사용할 수 있습니다. 이 경우 같은 코드 인스턴스를 사용하여 계산을 실행하는 여러 스레드를 만듭니다. Visual Basic 6.0과 같은 언어에서는 이와 같은 작업이 거의 불가능하므로 자유 스레드 응용 프로그램을 작성한 경험이 있는 응용 프로그램 개발자는 아마 C++ 개발자뿐일 것입니다.

스레딩 모델 작업

스레딩 모델에 대한 개념의 이해를 돕는 예로 한 집에서 다른 집으로 이사하는 일을 들 수 있습니다. 단일 스레드 방법은 포장에서 상자 운반과 짐 풀기까지의 모든 일을 직접하는 것이라고 볼 수 있습니다. 아파트 스레딩 모델로 작업하는 경우는 절친한 친구 몇에게 도움을 청하는 것과 같습니다. 각 친구는 각기 다른 방에서 일하고 다른 방에서 일하는 사람을 도울 수 없습니다. 그들은 각자의 공간과 이삿짐을 맡습니다. 자유 스레드 방법을 선택하는 경우, 친구들에게 도움을 요청하는 것은 아파트 스레딩 모델과 동일하지만 친구들이 모두 어느 시간이나 어느 방에서든 함께 이삿짐을 꾸릴 수 있다는 점이 다릅니다. 이 비유에서 집은 모든 스레드가 작동하는 프로세스이고 각 친구는 코드의 인스턴스이며 이삿짐은 응용 프로그램의 리소스와 변수입니다.

위의 예에서는 각 모델의 장점과 단점을 보여 줍니다. 아파트 스레딩은 구성 요소의 여러 인스턴스가 작동하므로 단일 스레딩보다 빠릅니다. 자유 스레딩에서는 모든 일이 동시에 일어나고 모든 리소스가 공유되므로 어떤 경우에는 아파트 스레딩보다 빠르고 훨씬 효율적입니다. 그러나 여러 스레드에서 공유 리소스를 변경하는 경우 문제가 일어날 수 있습니다. 한 사람이 상자를 사용해 부엌 물건을 싼 다음 다른 친구가 와서 같은 상자에 침실 물건을 포장하는 경우를 생각해 보십시오. 첫 번째 친구는 상자에 '부엌'이라는 레이블을 붙였는데 뒷 친구가 그 위에 '침실'이라는 레이블을 붙입니다. 결국 짐을 풀 때는 부엌 물건을 침실에서 풀게 될 것입니다.

예제 응용 프로그램

첫 단계로 예제 응용 프로그램의 디자인을 검토합니다. 이 응용 프로그램에서는 여러 스레드를 만들고 각 스레드는 MSMQ 대기열의 메시지를 수신합니다. 이 예제에서는 기본 Form 클래스와 사용자 지정 MQListen 클래스의 두 가지 클래스를 사용합니다. Form 클래스는 사용자 인터페이스를 처리하는 것과 아울러 작업자 스레드를 만들고 관리하고 소멸시킵니다. MQListen 클래스에는 메시지 대기열 항목을 비롯해 작업자 스레드를 실행하는 데 필요한 모든 코드가 포함됩니다.

응용 프로그램 준비

  • 응용 프로그램을 시작하려면 Visual Studio .NET을 열고 MultiThreadedMQListener라는 C# Windows 응용 프로그램을 새로 만듭니다. 폼의 속성을 열고 이름을 QueueListenerForm으로 지정합니다. 초기 폼이 그려지면 그 위에 레이블 두 개, 단추 두 개, 상태 표시줄 한 개 및 텍스트 상자 두 개를 끌어 놓습니다. 첫 번째 텍스트 상자는 Server로, 두 번째는 Queue로 이름을 지정합니다. 첫 번째 단추는 StartListening로, 두 번째는 StopListening으로 이름을 지정합니다. 상태 표시줄은 기본 이름인 statusBar1로 그대로 두어도 됩니다.
  • 그런 다음 프로젝트 메뉴에서 참조 추가를 클릭하여 System.Messaging 네임스페이스에 대한 참조를 추가합니다. .NET 구성 요소 목록에서 System.Messaging.Dll을 찾아 선택합니다. 이 네임스페이스에는 MSMQ 대기열과 통신하는 데 사용되는 클래스가 포함됩니다.
  • 그런 다음 파일 메뉴에서 새 항목 추가를 클릭하여 프로젝트에 새 클래스를 추가합니다. Class 템플릿을 선택하고 이름을 MQListen으로 지정합니다. 클래스 맨 위에 다음과 같은 using 문을 추가합니다.
    // C#
    using System.Threading;
    using System.Messaging;

    System.Threading 네임스페이스를 사용하여 필요한 모든 스레딩 기능(이 경우, Thread 클래스와 ThreadInterruptException 생성자)에 액세스할 수 있습니다. 이 네임스페이스에서 사용할 수 있는 다른 많은 추가 기능이 있으나, 이 기사에서는 이에 대해 다루지 않습니다. System.Messaging 네임스페이스를 사용하면 대기열의 메시지 보내기 및 받기를 포함한 MSMQ 기능에 액세스할 수 있습니다. 이 예제에서는 MessageQueue 클래스를 사용하여 메시지를 수신합니다. 또한 기본 폼 코드에 using System.Threading도 추가합니다.

모든 참조가 준비가 되면 코드 작성을 시작할 수 있습니다.

작업자 스레드

첫 단계로 모든 스레드 작업을 캡슐화하는 MQListen 클래스를 작성합니다. 다음 코드를 MQListen대해서도 삽입합니다.

// C#
public class MQListen
{   
   private string m_MachineName;
   private string m_QueueName;
      
   // 생성자는 필요한 대기열 정보를 적용합니다.
   public MQListen(string MachineName, string QueueName)
   {
      m_MachineName = MachineName;
      m_QueueName = QueueName;
   }
    

   // 각 스레드에서 MQ 메시지를 수신하는 데 사용하는 유일한 메서드입니다. 
   public void Listen()
   {
      // MessageQueue 개체를 만듭니다.    
System.Messaging.MessageQueue MQ = new System.Messaging.MessageQueue(); // MessageQueue 개체의 경로 속성을 설정합니다.
MQ.Path = m_MachineName + "\\private$\\" + m_QueueName; // Message 개체를 만듭니다. System.Messaging.Message Message = new System.Messaging.Message(); // 인터럽트가 수신될 때까지 반복합니다. while (true) { try { // 인터럽트가 throw된 경우 catch하기 위해 대기합니다. System.Threading.Thread.Sleep(100); // Message 개체를 receive 함수의 결과와 동일하게 설정합니다. // Timespan(일, 시간, 분, 초).
Message = MQ.Receive(new TimeSpan(0, 0, 0, 1)); // 받은 메시지의 레이블을 표시합니다. System.Windows.Forms.MessageBox.Show(" Label: " + Message.Label); } catch (ThreadInterruptedException e) { // 기본 스레드에서 ThreadInterrupt를 catch하고 끝냅니다.
Console.WriteLine("Exiting Thread"); Message.Dispose(); MQ.Dispose(); break; } catch (Exception GenericException) { // receive에서 throw된 예외를 catch합니다.
Console.WriteLine(GenericException.Message); } } } }

코드 설명

MQListen 클래스에는 생성자 이외에 함수가 하나 있습니다. 이 함수는 각 작업 스레드가 실행하는 모든 작업을 캡슐화합니다. 스레드가 시작될 때 이 함수가 실행될 수 있도록 기본 스레드에서 이 함수에 대한 참조를 스레드 생성자로

전달합니다. Listen에서는 우선 메시지 대기열 개체를 설정합니다. MessageQueue 생성자는 세 가지 구현으로 오버로드됩니다. 첫 번째 구현 작업은 수신할 대기열의 위치를 지정하는 문자열 인수와 대기열을 처음으로 액세스하는 응용 프로그램에 대기열에 대한 단독 읽기 권한을 부여해야 하는지 여부를 지정하는 부울 등 두 가지 인수를 취합니다. 두 번째 구현은 대기열 경로 인수만을 취하고 세 번째 구현은 아무 인수도 취하지 않습니다. 간단히 하기 위해 세 번째 구현을 사용하여 다음 줄에 경로를 할당할 수 있습니다.

대기열에 대한 참조를 마쳤으면 메시지 개체를 만들어야 합니다. 메시지 생성자도 세 가지 구현을 가집니다. 처음 두 구현은 대기열에 메시지를 쓸 경우에 사용할 수 있습니다. 이 구현에서는 메시지 본문에 들어갈 개체와 메시지 본문으로 개체가 serialize되는 방법을 정의하는 IMessageFormatter 개체를 취합니다. 여기서는 대기열에서 읽는 중이므로 빈 메시지 개체를 초기화합니다.

개체를 초기화한 후 모든 작업을 수행할 기본 루프를 입력합니다. 나중에 기본 스레드에서 이 스레드를 중지시키기 위해 Interrupt를 호출하면 스레드가 대기, 중지 또는 조인 상태에 있는 경우에만 중단됩니다. 세 가지 상태 중 하나에 있지 않은 경우 다음에 이런 상태로 들어가기 전까지는 중단되지 않습니다. 작업자 스레드가 대기, 중지 또는 조인 상태를 입력하도록 하려면 System.Threading 네임스페이스에 있는 Sleep 메서드를 호출합니다. Sleep 메서드는 이전에 Windows API sleep 함수를 사용한 경험이 있는 C++ 개발자와 Visual Basic 개발자에게는 아주 익숙할 것입니다. 이 메서드는 스레드가 대기하는 시간(밀리초)이라는 단일 인수를 취합니다. Sleep 메서드를 호출하지 않으면 작업자는 인터럽트 요청을 받을 수 있는 상태를 입력할 수 없고 프로세스를 수동으로 종료하기 전까지는 무기한 계속됩니다.

MQ Receive 메서드의 구현은 두 가지입니다. 첫 번째 구현은 아무것도 취하지 않으며 메시지를 수신할 때까지 무한히 대기합니다. 이 예제에서 사용되는 두 번째 구현에서는 TimeSpan 개체로 제한 시간을 지정합니다. TimeSpan 생성자에는 일, 시간, 분, 초 등 네 가지 인수가 있습니다. 이 예제에서는 Receive 메서드가 1초 동안 대기한 후 시간이 초과되어 반환됩니다.

메시지가 수신되면 메시지는 앞에서 만든 메시지 개체에 할당되어 처리에 사용될 수 있습니다. 이 예제에서는 해당 레이블이 있는 메시지 상자를 열고 메시지를 삭제합니다. 이 코드를 실제 사용을 위해 변경하려면 여기에 메시지 처리 코드를 넣습니다.

작업자 스레드에서 Interrupt 요청을 수신하면 ThreadInterruptedException 예외를 throw합니다. 해당 예외를 catch하려면 Sleep 및 Receive 함수를 try-catch 블록에 래핑합니다. 두 가지 catch를 지정해야 합니다. 첫째는 인터럽트 예외를 catch하고 둘째는 catch되는 오류 예외를 처리합니다. 인터럽트 예외가 catch되면 우선 스레드가 끝나는 디버그 창에 씁니다. 그런 다음 대기열 개체와 메시지 개체에서 Dispose 메서드를 호출하여 모든 메모리를 정리하고 가비지 수집기로 보냅니다. 마지막으로 while 루프를 빠져 나갑니다.

함수에서 while 루프를 끝내자마자 관련 스레드는 코드 0으로 종료합니다. 디버그 창에 'The thread '<name>' (0x660) has exited with code 0 (0x0)'와 같은 메시지가 나타납니다. 스레드는 이제 컨텍스트를 벗어났으며 자동으로 소멸됩니다. 기본 스레드와 작업 스레드 중 어디에서도 정리하기 위해 별도로 해야 할 일은 없습니다.

기본 폼

다음 단계에서는 폼에 코드를 추가하여 작업자 스레드를 만들고 각 스레드에서 MQListen 클래스를 시작합니다. 우선 폼에 다음과 같은 함수를 추가합니다.

// C#
private void StartThreads()
{
   int LoopCounter; // 스레드 수
   StopListeningFlag = false; // 작업자가 중지될지 여부를
                               // 추적하는 플래그입니다.

   	// 작업자 스레드가 될 5개의 스레드 배열을 선언합니다.    
Thread[] ThreadArray = new Thread[5]; // 작업자 스레드의 모든 코드를 포함하는 클래스를 선언합니다.
MQListen objMQListen = new MQListen(this.ServerName.Text,this.QueueName.Text); for (LoopCounter = 0; LoopCounter < NUMBER_THREADS; LoopCounter++) { // Thread 개체를 만듭니다.
ThreadArray[LoopCounter] = new Thread(new ThreadStart(objMQListen.Listen)); // 스레드를 시작하면 ThreadStart 대리자를 호출합니다.
ThreadArray[LoopCounter].Start(); } statusBar1.Text = LoopCounter.ToString() + " listener threads started"; while (!StopListeningFlag) { // 사용자가 중지 단추를 누를 때까지 대기합니다. // 대기하는 동안 시스템에서는 다른 이벤트를 처리할 수 있습니다.
System.Windows.Forms.Application.DoEvents(); } statusBar1.Text = "Stop request received, stopping threads"; // 인터럽트 요청을 각 스레드에 보냅니다. for (LoopCounter = 0;LoopCounter < NUMBER_THREADS; LoopCounter++) { ThreadArray[LoopCounter].Interrupt(); } statusBar1.Text = "All Threads have been stopped"; }

코드 설명

이 함수에서는 먼저 5개의 항목으로 구성된 스레드 배열을 만듭니다. 이 배열은 나중에 사용할 수 있도록 모든 스레드 개체에 대한 참조를 보관합니다.

MQListen 클래스의 생성자는 메시지 대기열을 호스팅하는 컴퓨터 이름과 수신할 대기열의 이름 등 두 가지 인수를 취합니다. 이 생성자는 텍스트 상자의 값을 사용하여 이 인수를 할당합니다.

스레드를 만들려면 각 스레드 개체를 초기화할 루프를 입력합니다. 스레드의 Start 메서드가 호출될 때 호출될 함수를 가리키는 대리자를 Thread 생성자에 전달해야 합니다. MQListen.Listen 함수를 사용하여 스레드를 시작할 수 있지만 이 함수는 대리자가 아닙니다. 스레드 생성자의 요구 사항을 충족하려면 ThreadStart 개체를 전달하여 지정된 함수 이름으로 대리자를 만들어야 합니다. 여기서는 MQListen.Listen 함수에 대한 참조를 ThreadStart 개체에 전달합니다. 이 배열 요소가 초기화되었으므로 곧바로 Start 메서드를 호출하여 스레드를 시작합니다.

스레드가 모두 시작되면 폼의 상태 표시줄을 적절한 메시지로 업데이트합니다. 스레드가 실행되면서 대기열을 수신하고 있는 동안 기본 스레드는 사용자가 응용 프로그램에 수신을 중단하도록 요청할 때까지 대기합니다. 이렇게 하려면 사용자가 StopListening 단추를 클릭할 때까지 while 루프를 입력하여 StopListeningFlag값을 변경합니다. 이 대기 루프에서 응용 프로그램은 Forms.Application.DoEvents 메서드를 사용하여 필요한 다른 모든 처리를 수행할 수 있습니다. 이것은 Visual Basic에 익숙한 사람에게는 이전의 DoEvents 메서드와 같고, C++에 익숙한 사람에게는 MSG 펌프를 작성하는 것과 동일합니다.

StopListening 단추를 클릭하면 이 루프가 끝나고 스레드 종료 코드로 진입합니다. 스레드를 모두 종료하기 위해 코드에서는 스레드 배열 내에서 루프하여 각 스레드에 인터럽트 신호를 보냅니다. 이 루프 내에서 배열의 각 스레드 개체에 대해 Interrupt 메서드를 호출합니다. 이 메서드가 호출되기 전까지는 MQListen 클래스의 코드는 정상적으로 실행됩니다. 이 때문에 다른 것을 처리하는 도중에라도 각 작업자에 대해 Interrupt를 호출할 수 있습니다. 스레드 클래스는 스레드가 완료되면 각 스레드의 정리를 처리합니다. 마지막 작업으로 기본 폼의 상태 표시줄을 업데이트하고 종료합니다.

이제 단추 뒤에 코드를 추가해야 합니다. 다음과 같은 코드를 StartListening 단추의 Click 이벤트에 추가합니다.

// C#
statusBar1.Text = "Starting Threads";
StartThreads();

이 코드는 상태 표시줄을 업데이트하고 StartThreads 메서드를 호출합니다. 이 코드에서 StopListening 단추의 StopListeningFlag를 True로 설정하기만 하면 됩니다.

// C#
StopListeningFlag = true;

마지막 단계로 StopListeningFlag에 대해 폼 수준 변수를 추가합니다. 폼 코드 맨 위에 다음 줄을 추가합니다.

// C#
private bool StopListeningFlag = false;

응용 프로그램을 테스트하려면 메시지 대기열에 쓰기 위한 예제 응용 프로그램인 MQWrite를 다운로드합니다.

다중 스레드 코드의 문제

샘플 코드 작업을 완료했으므로, 이제 다중 스레드 응용 프로그램을 작성하는 데 필요한 도구를 가지게 되었습니다. 스레딩은 응용 프로그램의 성능과 확장성을 극적으로 개선할 수 있습니다. 이런 강력함과 더불어 스레딩의 위험에 대해서도 알고 있어야 합니다. 어떤 경우에는 스레드 사용으로 응용 프로그램에 해를 끼칠 수 있습니다. 스레드로 인해 작업이 다운되거나 예상치 못한 결과가 일어날 수 있으며 심지어 응용 프로그램이 응답을 중지할 수도 있습니다.

스레드가 여러 개인 경우 스레드 서로가 특정 지점에 도달하거나 종료할 때까지 대기하고 있지 않은지 확인합니다. 제대로 수행하지 않으면 각 스레드가 서로 대기하고 있으므로 아무 스레드도 종료되지 않는 교착 상태를 초래할 수 있습니다.

여러 스레드에서 쉽게 공유될 수 없는 리소스(예: 플로피 디스크 드라이브, 직렬 포트 또는 적외선 포트)에 액세스해야 하는 경우 스레드 사용을 방지하거나 synclock 또는 mutex와 같은 고급 스레딩 도구를 사용하여 동시성을 관리할 수 있습니다. 두 스레드가 이 리소스 중 하나를 동시에 액세스하려 하면 한 스레드에서는 리소스를 얻을 수 없고 데이터 손상이 일어납니다.

스레딩의 또 다른 일반적인 문제는 경쟁 조건입니다. 한 스레드가 파일에 데이터를 쓰는 동안 다른 스레드에서 해당 파일을 읽고 있는 경우 어떤 스레드가 먼저 종료할지 알 수 없습니다. 이 문제는 두 스레드가 파일 끝으로 경주하고 있기 때문에 경쟁 조건이라고 합니다. 읽는 스레드가 쓰는 스레드보다 앞서는 경우 알 수 없는 결과가 반환됩니다.

스레드를 사용할 때 모든 스레드가 다른 스레드와 별도로 완전히 작업을 마칠 수 있을지 여부도 생각해 보아야 합니다. 데이터를 앞뒤로 전달해야 하는 경우 데이터가 비교적 간단해야 합니다. 복잡한 개체를 전달하는 경우 이 개체를 앞뒤로 이동하기 위해 막대한 마샬링 비용이 들어가기 시작합니다. 이에 따라 운영 체제에서 관리하는 데 오버헤드가 생기고 전체 성능이 떨어집니다.

또 다른 문제로는 코드를 다른 개발자에게 넘겨주는 데 따른 전환 비용이 있습니다. .NET으로 스레딩이 훨씬 쉬워졌지만 코드를 유지 관리하는 다음 개발자가 스레딩 작업을 하기 위해서는 스레딩에 대해 알고 있어야 합니다. 그러나 이 점은 스레드 사용을 피해야 할 이유라기 보다는 적절한 코드 주석을 제공해야 할 이유입니다.

이런 문제들로 인해 스레딩 자체를 사용하지 않는 것 보다는 응용 프로그램을 디자인하고 스레드를 사용할지 여부를 결정할 때 모든 문제들을 기억하고 있는 것이 좋습니다. 불행히도 이 백서에서는 이런 문제를 방지하기 위한 방법을 설명하지 않습니다. 스레드를 사용하기로 했으나 위에서 언급한 문제가 발생한 경우 synclock이나 mutex를 검토하여 문제를 해결하거나 다른 솔루션을 찾아 보십시오.

결론

여기에 있는 내용으로 스레딩을 이용하는 응용 프로그램을 작성할 수 있습니다. 그러나 이렇게 하는 동안 관련된 문제를 기억하십시오. 스레딩은 제대로 사용하면 응용 프로그램의 성능과 확장성을 단일 스레드 응용 프로그램에 비해 훨씬 개선할 수 있지만, 올바로 사용하지 못하는 경우 정반대의 결과를 가져올 수 있으며 응용 프로그램이 불안정하게 될 수 있습니다.


[이 자료는 MSDN 라이브러리에서 가져왔습니다.]

"C#" 카테고리의 다른 글
  • Event Handling in .NET Using C# (0)2007/06/26
  • C# 명령줄 컴파일러 옵션 (0)2007/05/03
  • C# 스레드 사용 (1)2007/02/05
  • Office 2003 : Visual Studio Tools for Office Sy... (0)2007/02/05
  • 웹 서비스의 세계로 - 구글 검색을 활용해보자. (0)2007/02/05
2007/02/05 17:52 2007/02/05 17:52
Posted by webdizen
Tags Thread
No Trackback 1 Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2604

Leave your greetings.

  1. 니눅스

    오늘 스레딩 공부하다가 막히는 부분이 있었는데, 잘 정리된 글 잘 읽고 갑니다.

    2008/08/03 23:55 [ Permalink : Modify/Delete : Reply ]
[로그인][오픈아이디란?]

Programming/C#2007/02/05 17:36

Office 2003 : Visual Studio Tools for Office System을 활용한 Excel 게임

고수닷넷 - 디버깅전문가님

요약

Microsoft에서는 Microsoft Office Word 2003과 Excel 2003을 새로운 개발 플랫폼으로 사용하기 위한 작업을 진행 중이다. Office 2003을 개발 플랫폼으로 사용한다는 의미는 과연 무엇일까? 이제 우리는 Office 2003에서 Windows Forms를 다루듯이 버튼과 메뉴를 추가하는 것과 같이 Windows Forms가 제공하는 거의 모든 기능을 사용할 수 있을 뿐만 아니라 Office 2003(특히, Excel과 Word)에서 제공하는 거의 모든 기능을 프로그래밍 적으로 다룰 수 있다는 것을 의미한다. Microsoft에서는 Visual Studio Tools for the Microsoft Office System(줄여서 VSTO)이라고 불리는 이 기술을 통해서 데스크 톱의 기능성과 생산성을 높이고자 한다.

이 글에서는 엑셀에서 작동하는 게임을 작성하는 방법에 대해서 소개하고 Excel 문서를 다루는 방법을 소개한다.

Microsoft는 Office 2003을 출시한 이후로 VSTO에 대한 많은 내용들을 소개하였다. 필자는 지난 1월에 있었던 Office 기술 관련 세미나에 참석하여 처음으로 VSTO에 대해서 알게 되었다. 그 세미나에서 여러 가지 데모를 볼 수 있었는데, 대부분은 주로 엔터프라이즈 환경을 위한 예제였다. 하지만 아이러니 하게도 필자는 그 데모를 보면서 90년대 초반 인기를 끌었던 Tetris라는 게임이 머리를 스쳤다. ‘아! 딱딱하지 않으면서도 사용자들에게 VSTO의 가능성을 보여주기에는 게임만큼 쉬운 방법이 없지!’ 그렇게 해서 Excel Tetris 게임을 만들게 되었다.

사용자 삽입 이미지

<Figure 1 - Tetris>

정보 구하기

여러분도 알고 있듯이, VSTO는 Visual Studio .NET 2003과 함께 설치되지 않는다. VSTO는 Microsoft Office System을 위한 확장 기능일 뿐, .NET Platform을 위한 개발 도구는 아니기 때문이다. VSTO는 현재 일반 유저에게 공개되어 있지 않으며, MSDN Subscription Download 사이트에서 다운로드할 수 있다. VSTO를 설치하기 위해서는 다음과 같은 프로그램들이 여러분의 운영체제에 미리 설치되어 있어야 한다.

- Visual Studio .NET 2003

- Microsoft Office System

또한 Office System의 경우, 전체 설치를 하거나 custom 설치를 눌러서 Excel이나 Word에서 .NET Programmability Support를 선택하도록 한다. 또한 VSTO를 사용하여 개발한 솔루션을 배포하기 위해서는 다음과 같은 프로그램들을 순서대로 설치해야 한다.

- Microsoft .NET Framework 1.1

- Microsoft Office Excel 2003(앞서 말한 것과 같이 필요한 요소들을 설치해야 한다)

현재 VSTO는 언어별로 제공하고 있기 때문에, 만약 여러분이 영문 운영 체제에서 작업하는 것이 아니라면 반드시 언어 환경과(Culture) 일치하는 VSTO를 설치해야 한다. VSTO에 대한 개괄적인 정보들을 얻기 위해서 다음 사이트를 방문해 보는 것이 좋을 것이다. http://msdn.microsoft.com/office/unders ··· ntro.asp 또한 Office 홈페이지(http://msdn.microsoft.com/office/)에 가면 간단한 예제부터 시작해서 VSTO에 대한 최근의 정보를 확인할 수 있다. 만약 여러분이 Visual Studio .NET 2003 을 설치했다면(이에 대한 정보는 VSTO 설치 파일의 도움말 html에서 확인 해야 함), 로컬 컴퓨터에 설치되어 있는 MSDN 도움말에서 간단한 정보를 얻을 수 있다. 하지만 충분한 내용을 제공한다고 볼 수 없기 때문에 여러분이 구체적이고 실질적인 작업을 진행하기에는 다소 어려움이 따를 것으로 보인다. 그리고 이 글에서 설치나 배포, 그리고 VSTO의 작동 방법에 대해서는 자세하게 소개하고 싶지만, 이 글은 VSTO를 활용하는 실질적인 예를 보여주기 위한 것이기 때문에 관련 정보에 대한 링크를 걸어두는 것으로 만족하도록 하겠다.

시작은 Windows Forms부터

이번 글에서 소개하는 Excel Tetris를 작성하기 전에 필자는 다음과 같은 기능들을 보여주고자 했다.

- 기본적인 Tetris 게임 진행

- 타이머 객체를 활용

- 엑셀의 Row와 Column을 다루는 방법

- 이벤트 처리

이 예제를 작성하기 전에 필자도 여러분처럼 VSTO에 익숙하지 않았기 때문에 VSTO를 기본 테스트 프로젝트로 사용하고 싶지는 않았다. 코드의 작성도 용이하고 디버깅도 쉬워야 했기 때문에 VSTO를 사용하는 대신 Windows Forms에서 간단한 게임을 구현하고 난 후, 이를 VSTO에 적용하는 방법을 선택하기로 하였다. 여러분이 Tetris를 한번이라도 해 본적이 있다면, 이 게임이 굉장히 직관적이며 아주 간단한 조작만이 요구되기 때문에 누구나 쉽게 접근할 수 있다는 것을 알고 있을 것이다. 그렇기 때문에 프로그래밍을 시작하는 많은 사람들이 이 게임을 통해서 알고리즘에 대해서 이해하고 코드를 작성하는 방법에 대해서 이해하려고 한다. 사실 Tetris를 구현하는 소스는 이미 일반에게 많이 공개되었으며, 굉장히 많은 알고리즘이 사용되고 있다. 이 책에서 제공하는 소스도 필자가 나름대로 고민하고 생각하여 구현한 소스로 최고의 알고리즘을 사용했다고 단언할 수는 없다. 또한 필자는 이 소스를 구현함에 있어서 어느 누구의 소스도 참고하지 않았다는 점을 미리 말해둔다.

Tetris 게임을 한번도 해보지 않은 사람을 위해서 게임 진행 방법에 대해서 간단히 소개하자면, 이 게임은 총 4개의 블록으로 구성된 7가지 형태의 바(bar)가 존재한다. 각 바들은 각자의 특성에 따라서 여러 가지 형태로 변형이 되며 여러분이 Row에 빈 곳이 없도록 Bar를 채우는 것이 목표이다. 이번 글이 테트리스 소스 자체에 설명하기 위함이 아니기 때문에 보다 자세한 내용은 소스 코드를 참고하도록 한다. 필자가 이 소스를 구현하면서 가장 중점적으로 생각했던 부분은 이 소스를 VSTO 프로젝트에서 사용해야 하기 때문에 각 블록을 화면에 그리기 위한 코드를 VSTO에 적용하기 쉽도록 정리하는 부분이었다. 이 소스 코드는 FillCell 이라는 이름으로 정리하였다. 이 함수에 대해서는 뒤에서 자세하게 소개할 것이다. Windows Forms에서 만든 예제의 실행 화면은 다음과 같으며, 뒤에서 이 화면에 어떻게 Excel에서 표현되는지 확인할 수 있을 것이다.

사용자 삽입 이미지

<Figure 2 - Windows Forms 예제 파일>

간단한 예제 만들기

이 글은 VSTO에 이미 익숙한 개발자보다는 VSTO에 익숙하지 않은 개발자 혹은 VSTO에 대해서 들어본 적이 없는 개발자를 위한 글이기 때문에 VSTO가 무엇인지에 대한 기본적인 개념을 얻을 수 있도록 간단한 프로젝트를 만드는 것부터 시작하겠다. VSTO를 설치하고 난 후 새 프로젝트를 생성하면 다음 <Figure 3>과 같이 새로운 템플릿이 추가된 것을 확인할 수 있다. 여러분은 사용하는 언어에 따라서 C# 또는 VB.NET으로 프로젝트를 생성할 수 있다. 우선 C# 프로젝트를 선택하고 엑셀 문서를 선택한 다음 프로젝트 이름을 입력하면 프로젝트 이름의 엑셀 문서와 엑셀 문서에서 사용할 DLL을 만들기 위한 C# 프로젝트가 생성된다.

사용자 삽입 이미지

<Figure 3 - 추가된 프로젝트 템플릿>

프로젝트를 생성하자 마자, 디버그-시작 메뉴를 클릭하거나 Ctrl+F5를 클릭하여 프로젝트를 시작해보자. 여러분이 그 동안 접해왔던 Windows Forms나 익스플로어(Web Forms를 위한 호스트) 창이 아니라 엑셀 문서가 열린다. 프로젝트 마법사가 생성한 코드에는 여러분이 아무것도 추가한 내용이 없기 때문에 여러분이 앞으로 어떻게 해야 할지 전혀 알 수 없는 백지 상태의 엑셀 문서가 열린다. 여러분이 무엇을 해도 상관없지만 엑셀 문서를 닫기 전에 저장하지는 않도록 한다. 나중에 이 문서는 C# 프로젝트에서 다루기 위해서 수정할 것이다. 일단 문서를 닫고 이번에는 디버그-디버깅 모드로 시작 또는 F5를 클릭하여 디버깅 모드로 프로젝트를 시작해보자. Visual Studio .NET이 디버깅 모드로 전환되고 이전과 마찬가지로 엑셀 파일이 열린다. 이번에는 엑셀 파일을 닫지 말고 Visual Studio .NET에서 디버그-디버깅 중지를 눌러보자. 기존에 여러분이 작업했던 것과 마찬가지로 엑셀 문서가 닫힐 것이다.

이번에는 백지 상태의 엑셀 문서에 버튼을 추가해보자. 지금까지 살펴본 모든 작동 방법들이 Windows Forms과 거의 동일하기 때문에 여러분은 엑셀에서 버튼을 추가하고 이벤트 핸들러는 연결하는 작업이 Windows Forms의 작업에서처럼 간단할 것이라고 예상하겠지만, 불행하게도 VSTO는 이러한 작업을 위한 마법사 기능을 제공하고 있지 않다. 결론적으로 볼 때 C# 소스에 추가되는 코드는 동일하지만, 여러분이 이 모든 작업을 수작업을 해야 한다는 사실이 불편할 뿐이다. VSTO 팀에서 다음 버전에서는 엑셀을 Windows Forms처럼 사용할 수 있는 마법사를 제공해주기를 바란다.

UI 구현하기

엑셀에서 버튼을 추가하기 위해서는 Toolbar에서 Control Toolbox를 선택하여 Control Toolbox를 띄우도록 한다. 다음 <Figure 4>에서 확인할 수 있듯이 엑셀 문서에 여러 가지 컨트롤들을 추가할 수 있으며, More Controls를 클릭하여 <Figure 4>에 있는 컨트롤 이외에도 훨씬 많은 컨트롤들을 포함시킬 수 있다. <Figure 5>와 같이 버튼을 추가하고 속성 버튼을 눌러 Text에 Click Here를 입력하고 ID는 btnClick으로 설정한다. 이 버튼을 누르면 Sheet1의 A1에 ‘Hello, Excel’이라는 텍스트를 입력하도록 할 것이다. 버튼을 추가하였다면 Control Toolbox와 Property 창을 닫고 엑셀 문서를 저장하도록 한다.

사용자 삽입 이미지

< Figure 4 - Control Toolbox>

사용자 삽입 이미지

< Figure 5 - Command Button 추가>

이번에는 C# 프로젝트로 돌아와서 엑셀 문서에 추가한 버튼 객체를 찾고 이벤트 핸들러를 설치해야 한다. 여러분이 C# 소스 코드를 살펴보았다면 이미 알고 있겠지만, 엑셀 문서에 있는 컨트롤을 찾기 위하여 FindControl 이라는 함수를 사용할 것이다. 이 함수는 다음 <Figure 6>과 같이 구현되어 있다.

/// <summary>

/// Finds the control with the specified name in the active worksheet.

/// </summary>

/// <param name='name'>Name of the control to find.</param>

/// <returns>

/// Returns the specified control, or null if it is not found.

/// </returns>

object FindControl(string name )

{

  return FindControl(name, (Excel.Worksheet) ThisWorkbook.ActiveSheet);

}

/// <summary>

/// Returns the control with the specified name in the specified worksheet.

/// </summary>

/// <param name='name'>Name of the control to find.</param>

/// <param name='sheet'>Worksheet object that contains the control.</param>

/// <returns>

/// Returns the specified control, or null if it is not found.

/// </returns>

object FindControl(string name, Excel.Worksheet sheet )

{

  Excel.OLEObject theObject;

  try

  {

       theObject = (Excel.OLEObject) sheet.OLEObjects(name);

       return theObject.Object;

  }

  catch

  {

       // Returns null if the control is not found.

  }

  return null;

}

사용자 삽입 이미지

<Figure 6 ? Overloaded FindControl methods>

기본적으로 생성되는 C# 소스 코드에는 <Figure 6>과 같이 두개의 오버로드된 FindControl 메서드가 존재한다. 매개 변수가 2개인 메서드는 찾고자하는 컨트롤의 ID 이름과 컨트롤이 있는 Sheet를 매개 변수로 넘긴다. 매개 변수가 1개인 메서드는 현재 작업 중인 Sheet를 구해서 또 다른 FindControl 메서드를 호출한다. FindControl 메서드에서는 Sheet의 OLEObjects 메서드를 호출하여 OLE 객체를 구한 다음 반환하고 있다. FindControl이 반환하는 OLEObjects는 MSForms에서 제공하는 컨트롤과 1:1 매칭이 되는데, 예를 들면 Command Button은 CommandButton과 Text Control은 TextBox와 매칭된다. 또한 소스 코드에서 확인할 수 있는 것처럼 FindControl이 컨트롤을 찾지 못할 경우 null을 반환하기 때문에, FindControl을 사용할 때에는 항상 리턴값이 null인지를 비교한 다음 리턴된 객체를 사용하도록 해야 한다. 따라서 다음과 같이 코드를 작성하여 엑셀 문서에 추가한 컨트롤을 찾을 수 있다.

MSForms.CommandButton btnClick = (MSForms.CommandButton) FindControl("btnClick");

그리고 다음과 같이 이벤트 핸들러를 생성할 수 있다. 이벤트 핸들러를 작성하는 방법은 Wizard가 제공되지 않을 뿐, 여러분이 지금까지 Windows Forms이나 Web Forms에서 해왔던것과 동일한 방법으로 추가한다.

btnClick.Click += new Microsoft.Vbe.Interop.Forms.CommandButtonEvents_ClickEventHandler(btnClick_Click);

하지만 다행스럽게도 Visual Studio .NET의 IntelliSense 덕분에 이 긴 코드를 직접 입력하지 않고 <Tab> 키만 눌러도 자동으로 코드가 추가된다. 이제 앞서 말했듯이, 버튼을 클릭했을 때 발생하는 핸들러에서 엑셀 영역에 텍스트를 설정하는 코드를 작성해야 한다. 코드를 작성하기 전에 Excel의 구조에 대해서 간단히 살펴보도록 하자. 엑셀의 구조는 크게 Application과 Worksheet, Sheet 그리고 Range(또는 Cell)로 나눌 수 있다. Application은 Excel 응용 프로그램 자체를 나타내며, Worksheet는 여러분이 작성하거나 다루고 있는 문서를 나타낸다. Sheet는 하나의 문서 안에 들어 있는 각 페이지를 나타내고 마지막으로 Range는 각 페이지에 있는 Cell의 영역을 나타낸다. Range의 범위가 하나일 경우를 Cell이라고 한다. 이러한 구조 때문에 각 단위들은 자신들에게 고유한 이벤트와 속성들을 제공한다. 예를 들어, 여러분이 프로그램을 실행할 때 발생하는 이벤트들은 모두 Application에 존재하며, 문서를 열거나 닫을 때 발생하는 이벤트들은 모두 Worksheet에 존재한다. 따라서 Excel을 제대로 다루기 위해서는 이러한 구조를 이해하고 여러분이 원하는 이벤트와 속성이 어떤 범위에 속할 것인지를 판단할 필요가 있다.

사용자 삽입 이미지

< Figure 7 ? Excel의 구조>

< Figure 7>에서와 같이 Range는 하나 이상의 Cell을 포함하고 있다. 하지만 Excel에서는 주요작업 단위를 Range로 사용하기 때문에 A1에 문자열을 출력하기 위해서 A1에 해당하는 Range를 구하도록 한다. Cell 이름을 통해서 Range를 얻는 방법은 간단한다. 다음 코드와 같이 원하는 영역의 이름을 매개 변수로 전달하면 해당 영역 만큼 Range를 반환한다.

private void btnClick_Click ( )

{

       Excel.Worksheet sheet = (Excel.Worksheet) ThisApplication.Sheets.get_Item(1);

       Excel.Range rng = sheet.get_Range ( "A1", "A1" );

       rng.Value2 = "Hello, Excel";

}

필자가 이 예제를 작성할 때 텍스트를 설정하기 위한 메서드나 속성이 당연히 Text와 같은 이름의 이름을 갖고 있을 것이라고 생각했지만, 결국 Value2라는 속성이었다(Text 속성은 읽기 전용이다). VSTO 팀에서 Value2를 지원하는지 모르겠지만, 다음 버전에서는 이러한 부분들이 수정되었으면 하는 바람이다.

작성된 코드를 테스트하기 위해서 디버그-시작 메뉴를 클릭하여 엑셀 문서를 띄운 후, 버튼을 클릭보자. A1에 ‘Hello, Excel’이라는 문자열이 출력될 것이다. 이제 여러분은 내가 더 이상 설명할 필요도 없이 엑셀 문서를 다루는 방법을 알고 있을 것이다.

Let’s get started!

이미 VSTO에 대해서 알고 있는 독자라면 지루한 설명을 보느라 수고가 많았다. 이제부터 본격적으로 엑셀에서 작동하는 게임을 만들어보도록 하겠다. 가장 먼저 앞서 설명한 것과 같은 방법으로 ExcelTetris라는 이름으로 C#용 엑셀 프로젝트를 생성하도록 한다. 그리고 앞에서 작성한 예제의 테트리스 클래스(MyTetris)를 새로운 프로젝트에 추가하도록 한다. 아무런 문제없이 빌드되는지 확인하도록 한다. 앞으로 추가해야 하는 부분들이 다소 복잡할 수 있기 때문에 많은 문제점들이 한꺼번에 발생하지 않도록 틈틈히 빌드 오류를 수정하면 진행하도록 하겠다. 아마도 몇몇 독자들은 Windows Forms에서 사용한 코드를 그대로 사용할 수 있는가에 대해서 의문을 갖고 있을것이다. 물론, 코드를 전혀 수정하지 않고 MyTetris 클래스를 그대로 사용할 수는 없다. 가장 큰 이유는 MyTetris 클래스가 화면을 그리는 부분과 관련된 코드를 포함하고 있기 때문에 엑셀 문서에 블록을 그려주기 위해서는 이 부분을 수정해야 한다. 하지만 크게 걱정할 필요는 없다. 여러분이 해야 할 일은 FillCell 이라는 메서드를 Graphics 객체를 사용하지 않고 Excel.Sheet를 사용하도록 수정하는 것이다. 물론 여러분은 MyTetris 클래스를 상속 받은 새로운 ExcelTetris 클래스를 구현한 후, FillCell 메서드를 오버라이드 할 수 있다. 하지만 필자는 될 수 있으면 예제를 간단하게 유지하기 위해서 MyTetris 클래스의 FillCell 부분과 UI에 관련된 몇몇 코드를 수정하였다. 몇가지 코드를 수정한다면, 이 클래스를 Excel 프로젝트와 Windows Forms 프로젝트 모두에서 쉽게 사용할 수 있을 것이다. 또한 이 글이 테트리스 게임 자체가 아니라, VSTO의 활용법에 중점을 두고 있기 때문에 MyTetris 클래스에 몇가지 논리적인 오류가 있음을 알아두도록 한다.

MyTetris 클래스를 사용하는 외부에서는 MyTetris 클래스가 화면에 게임의 진행 상태를 그릴 수 있도록 Draw 메서드를 호출하며, Draw 메서드는 다음과 같이 구현되어 있다.

/// <summary>

/// This is the only drawing method that client can call.

/// </summary>

/// <param name="sheet">Excel.Sheet object that will be used to draw</param>

public void Draw ( ref Excel.Worksheet sheet )

{

       // Game over?

       //

       if ( IsEnd == false )

       {

              DrawBlock ( ref sheet );

              DrawNextBlock ( ref sheet  );

       }

}

Draw 메서드는 게임의 진행 상태를 그려야 하는 Sheet에 대한 reference 객체를 매개 변수로 받고, 게임이 종료되지 않았다면 전달받은 매개 변수를 이용하여 DrawBlock과 DrawNextBlock 메서드를 호출한다. DrawBlock과 DrawNextBlock 메서드의 코드는 첨부된 파일에서 확인할 수 있다. 이 두 메서드는 좌표를 계산한 후, FillCell 메서드를 호출한다. FillCell 메서드에 대해서는 뒤에서 보다 자세하게 살펴보도록 한다.

ExcelTestris 프로젝트를 생성한 후, 가장 먼저 해야 할 일은 엑셀 문서를 열고 UI를 구현하는 것이다. 엑셀 문서를 여는 방법은 ExcelTetris를 실행시키거나 프로젝트 폴더에 가서 직접 열 수 있다. 어느 방법을 통해서든지 문서를 수정하고 저장하면 된다. 엑셀 문서를 다음 < Figure 8>과 같이 같이 버튼들을 배치하도록 하고, 각 버튼들의 속성은 다음 <표 1>과 같다. 컨트롤들을 배치할 때 버튼의 위치가 Cell의 크기를 조절하는 부분(InitSheet 메서드)과 겹치게 되면 컨트롤의 크기가 변경되기 때문에 적절하게 배치해야 한다.

사용자 삽입 이미지

<그림 8 ? UI 구성>

컨트롤 클래스

ID

Text

Enable

CommandButton

btnGameStart

Start

Yes

CommandButton

btnGameStop

Stop

No

CommandButton

btnGamePause

Pause

No

CommandButton

btnAbout

About?

Yes

TextBox

txtInput

No

TextBox

txtClearLined

Yes

<표 1 ? UI 컨트롤 속성>

나중에 이 버튼들에 대한 Click 이벤트를 C# 소스에 추가할 것이다. < Figure 8>에서 확인할 수 있는 것처럼 밑 부분에는 텍스트 박스가 위치하고 있다. 여러분이 게임을 진행하기 위해서는 키보드 조작을 위한 입력을 받아야 하는데, Sheet 자체 내에서 키보드 입력을 직접 받을 수 없었기 때문에, 아무 내용도 출력하지 않는 텍스트 상자를 만든 후 이 텍스트 상자를 통해서 입력을 받을 수 있도록 처리하였다. 또한 < Figure 8>에는 프로그램 정보를 출력하는 텍스트 상자들이 있다. <Figure 8>과 같은 형태로 UI를 구성한 후에 엑셀 문서를 닫고 C# 프로젝트로 이동한다. 우선 이 컨트롤들을 사용하기 위해서 변수를 다음과 같이 선언해야 한다.

// Excel controls...

//

private MSForms.CommandButton btnGameStart;

private MSForms.CommandButton btnGameStop;

private MSForms.CommandButton btnGamePause;

private MSForms.CommandButton btnAbout;

private MSForms.TextBox txtInput;

private MSForms.TextBox txtClearLined;

변수를 선언하고 난 후, 엑셀 문서에서 해당 컨트롤들을 찾아서 할당해야 한다. 이와 같은초기화 작업은 엑셀 문서가 열리는 시점에서 수행해야 하며 이에 대한 이벤트 핸들러가ThisWorkbook_Open 이벤트 핸들러이다. 이 핸들러는 엑셀 프로젝트 생성시 자동으로 생성되며 문서에 대한 초기화 작업을 수행하기에 적당한 곳이다. 초기화에 관련된 모든 코드가 ThisWorkbook_Open 이벤트 핸들러에 있지만, 뒤에서 소개할 Sheet 자체에 대한 초기화나 Style에 관련된 내용은 별도의 메서드로 분리하였다. ThisWorkbook_Open 이벤트 핸들러의 전체 소스는 <Figure 9>와 같다.

/// <summary>

/// Called when the workbook is opened.

/// </summary>

protected void ThisWorkbook_Open()

{

  sheet = (Excel.Worksheet)this.ThisApplication.Sheets.get_Item(1);

       // Find controls...

       // Because there are no wizard to connect excel's controls

       // with code-behind's variables, you should connect these

       // controls manually like following code.

       //

       this.btnGameStart = (MSForms.CommandButton)this.FindControl(

              "btnGameStart");

       this.btnGameStop = (MSForms.CommandButton)this.FindControl(

              "btnGameStop");

       this.btnGamePause = (MSForms.CommandButton)this.FindControl(

              "btnGamePause");

       this.btnAbout = (MSForms.CommandButton)this.FindControl(

              "btnAbout");

       this.txtInput = (MSForms.TextBox)this.FindControl(

              "txtInput");

       this.txtClearLined = (MSForms.TextBox)this.FindControl(

              "txtClearLined");

       // Craete event handler...

       //

       if (this.btnGameStart != null &&

              this.btnGameStop != null &&

              this.btnGamePause != null &&

              this.btnAbout != null &&

              this.txtInput != null)

       {

              this.btnGameStart.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGameStart_Click);

             

              this.btnGameStop.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGameStop_Click);

              this.btnGamePause.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGamePause_Click);

              this.btnAbout.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnAbout_Click);

              this.txtInput.KeyDown += new                                                    MSForms.MdcTextEvents_KeyDownEventHandler (

                      txtInput_KeyDown );

             

       }

       // Create new styles...

       //

       CreateStyle ( );

       // Create new tetris object & set properties...

       //

       tetris = new MyTetris ();

       tetris.OffSetX = 5;

       tetris.OffSetY = 5;

      

       tetris.GameEnded += new MyTetris.GameEndedEventHandler ( tetris_GameEnded );

       tetris.GameStarted += new

              MyTetris.GameStartedEventHandler ( tetris_GameStarted );

       tetris.ClearLine += new MyTetris.ClearLineEventHandler ( tetris_ClearLine );

       // Initialize current sheet to show tetris game properly

       //

       InitSheet ( );

       // Create new timer

       //

       timer = new System.Windows.Forms.Timer();

       timer.Interval = 1000;

       timer.Tick += new System.EventHandler(timer_Tick);

}

사용자 삽입 이미지


<Figure 9 ? ThisWorkbook_Open Event handler>

FindControl 메서를 이용하여 컨트롤을 얻고 각각의 Command Button에 대해서 Click 이벤트를 Text Box에 대해서 KeyDown 이벤트 핸들러를 추가한다. 이벤트 핸들러를 추가할 때 어려웠던 점은 핸들러의 매개 변수가 무엇을