고급 C# 기술(C# 및 Java)

업데이트: 2007년 11월

C#은 고급 프로그래밍 기술을 사용할 수 있도록 인덱서, 특성, 대리자 같은 몇 가지 유용한 언어 기능을 제공합니다.

인덱서

인덱서를 사용하면 배열에서와 같은 방식으로 클래스 또는 구조체에 액세스할 수 있습니다. 예를 들어, 회사의 부서 하나를 나타내는 클래스를 생각해 볼 수 있습니다. 이 클래스에 부서의 모든 직원 이름이 들어 있는 경우 다음과 같이 인덱서를 사용하여 이러한 이름에 액세스할 수 있습니다.

sales[0] = "Nikki";
sales[1] = "Becky";

인덱서는 클래스 정의에서 예를 들어 다음과 같은 시그니처로 속성을 정의하여 활성화됩니다.

public string this [int index]  //indexer

그런 다음 일반적인 속성에 대해서와 마찬가지로 getset 메서드를 제공하면 인덱서를 사용할 때 참조할 내부 멤버를 이러한 접근자가 지정합니다.

간단한 다음 예제에서는 Department라는 클래스를 만듭니다. 이 클래스는 내부에서 문자열의 배열로 표현되는 해당 부서의 직원에 액세스하기 위해 인덱서를 사용합니다.

public class Department
{
    private string name;
    private const int MAX_EMPLOYEES = 10;
    private string[] employees = new string[MAX_EMPLOYEES];  //employee array

    public Department(string departmentName)  //constructor
    {
        name = departmentName;
    }

    public string this [int index]  //indexer
    {
        get
        {
            if (index >= 0 && index < MAX_EMPLOYEES)
            {
                return employees[index];
            }
            else
            {
                throw new System.IndexOutOfRangeException();
            }
        }
        set
        {
            if (index >= 0 && index < MAX_EMPLOYEES)
            {  
                employees[index] = value;
            }
            else
            {
                throw new System.IndexOutOfRangeException();
            }
        }
    }

    // code for the rest of the class...
}

그런 다음 이 클래스의 인스턴스를 만들고 다음 코드 예제에서와 같이 이 인스턴스에 액세스할 수 있습니다.

class TestDepartment
{
    static void Main()
    {
        Department sales = new Department("Sales");

        sales[0] = "Nikki";
        sales[1] = "Becky";

        System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
    }
}

출력 결과는 다음과 같습니다.

The sales team is Nikki and Becky

자세한 내용은 인덱서(C# 프로그래밍 가이드)를 참조하십시오.

특성

C#은 특성이라는 형식에 대한 선언 정보를 추가하기 위한 메커니즘을 제공합니다. 특성은 개념상 Java의 주석과 비슷합니다. 형식에 대한 추가 정보는 형식 정의 앞에 나오는 선언 태그 안에 포함됩니다. 다음 예제에서는 .NET Framework 특성을 사용하여 클래스나 메서드를 데코레이팅하는 방법을 보여 줍니다.

아래 예제에서 GetTime 메서드는 WebMethodAttribute 특성을 추가하여 XML Web services로 표시됩니다.

public class Utilities : System.Web.Services.WebService
{
    [System.Web.Services.WebMethod]  // Attribute
    public string GetTime()
    {
        return System.DateTime.Now.ToShortTimeString();
    }
}

WebMethod 특성을 추가하면 .NET Framework에서 이 함수를 호출하는 데 필요한 XML/SOAP 교환을 자동으로 처리합니다. 이 웹 서비스를 호출하면 다음과 같은 값이 검색됩니다.

<?xml version="1.0" encoding="utf-8" ?>

<string xmlns="http://tempuri.org/">7:26 PM</string>

다음 예제에서 Employee 클래스는 SerializableAttribute 특성을 추가하여 serializable로 표시됩니다. Salary 필드가 public으로 표시되어 있지만 NonSerializedAttribute 특성으로 표시되어 있으므로 serialize되지 않습니다.

[System.Serializable()]        
public class Employee  
{
    public int ID;
    public string Name;        
    [System.NonSerialized()] public int Salary; 
}

자세한 내용은 사용자 지정 특성 만들기(C# 프로그래밍 가이드)를 참조하십시오.

대리자

C++, Pascal 등의 언어에서는 런타임에 호출하려는 함수를 선택할 수 있는 함수 포인터라는 개념을 지원합니다.

Java에서는 함수 포인터의 기능을 어떠한 생성에도 제공하지 않지만 C#에서는 이러한 기능을 제공합니다. 대리자 인스턴스는 호출 가능한 엔터티인 메서드를 Delegate 클래스를 사용하여 캡슐화합니다.

인스턴스 메서드의 경우 대리자는 클래스와 해당 인스턴스에 대한 메서드가 들어 있는 인스턴스로 구성됩니다. 정적 메서드의 경우 호출 가능한 엔터티는 클래스와 이 클래스에 대한 정적 메서드로 구성됩니다. 따라서 대리자를 사용하여 임의의 개체에 대한 함수를 호출할 수 있습니다. 대리자는 개체 지향적이고 형식이 안전하며 보안이 유지됩니다.

대리자를 정의하고 사용하는 과정은 다음 세 단계로 진행됩니다.

  • 선언

  • 인스턴스화

  • 호출

다음 구문을 사용하여 대리자를 선언합니다.

delegate void Del1();

그런 다음 이 대리자를 사용하면 void를 반환하고 어떠한 인수도 사용하지 않는 임의의 함수를 참조할 수 있습니다.

마찬가지로, 문자열 매개 변수를 사용하고 long을 반환하는 임의의 함수에 대한 대리자를 만들려면 다음과 같은 구문을 사용합니다.

delegate long Del2(string s);

이 대리자를 다음과 같이 이 시그니처가 있는 임의의 메서드에 할당할 수 있습니다.

Del2 d;                // declare the delegate variable
d = DoWork;  // set the delegate to refer to the DoWork method

여기서 DoWork의 시그니처는 다음과 같습니다.

public static long DoWork(string name)

대리자 재할당

Delegate 개체는 변경할 수 없습니다. 즉, 한번 설정된 후에는 일치하는 시그니처를 변경할 수 없습니다. 그러나 두 시그니처가 동일하면 다른 메서드를 가리키도록 만들 수 있습니다. 이 예제에서는 새 대리자 개체에 d를 다시 할당하여 d에서 DoMoreWork 메서드를 호출합니다. 이러한 재할당은 DoWork와 DoMoreWork의 시그니처가 동일한 경우에만 수행할 수 있습니다.

Del2 d;                    // declare the delegate variable
d = DoWork;      // set the delegate to refer to the DoWork method
d = DoMoreWork;  // reassign the delegate to refer to the DoMoreWork method

대리자 호출

대리자를 호출하는 일은 비교적 간단합니다. 메서드 이름 대신 대리자 변수의 이름을 사용하기만 하면 됩니다. 이 예제에서는 11과 22을 값으로 사용하여 Add 메서드를 호출하고 변수 sum에 할당된 long 결과를 반환합니다.

Del operation;                 // declare the delegate variable
operation = Add;      // set the delegate to refer to the Add method
long sum = operation(11, 22);  // invoke the delegate

다음 예제에서는 대리자 작성, 인스턴스화 및 호출 과정을 보여 줍니다.

public class MathClass
{
    public static long Add(int i, int j)       // static
    {
        return (i + j);
    }

    public static long Multiply (int i, int j)  // static
    {
        return (i * j);
    }
}

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type

    static void Main()
    {
        Del operation;  // declare the delegate variable

        operation = MathClass.Add;       // set the delegate to refer to the Add method
        long sum = operation(11, 22);             // use the delegate to call the Add method

        operation = MathClass.Multiply;  // change the delegate to refer to the Multiply method
        long product = operation(30, 40);         // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

출력

11 + 22 = 33

30 * 40 = 1200

대리자 인스턴스에는 개체 참조가 들어 있어야 합니다. 위 예제에서는 개체 참조를 지정할 필요가 없도록 메서드를 정적 메서드로 선언하여 이 문제를 해결했습니다. 그러나 대리자가 인스턴스 메서드를 참조하는 경우에는 다음과 같이 개체 참조를 제공해야 합니다.

Del operation;                   // declare the delegate variable
MathClass m1 = new MathClass();  // declare the MathClass instance
operation = m1.Add;     // set the delegate to refer to the Add method

이 예제에서 Add 및 Multiply는 MathClass의 인스턴스 메서드입니다. MathClass의 메서드가 정적으로 선언되지 않은 경우 다음과 같이 MathClass의 인스턴스를 사용하여 대리자를 통해 이 메서드를 호출합니다.

public class MathClass
{
    public long Add(int i, int j)       // not static
    {
        return (i + j);
    }

    public long Multiply (int i, int j)  // not static
    {
        return (i * j);
    }
}

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type

    static void Main()
    {
        Del operation;                   // declare the delegate variable
        MathClass m1 = new MathClass();  // declare the MathClass instance

        operation = m1.Add;       // set the delegate to refer to the Add method
        long sum = operation(11, 22);      // use the delegate to call the Add method

        operation = m1.Multiply;  // change the delegate to refer to the Multiply method
        long product = operation(30, 40);  // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

출력

이 예제에서는 메서드가 정적 메서드로 선언된 이전 예제와 동일한 결과를 출력합니다.

11 + 22 = 33

30 * 40 = 1200

대리자 및 이벤트

.NET Framework에서도 Windows 또는 웹 응용 프로그램의 단추 클릭 이벤트 같은 이벤트 처리 작업을 위해 대리자를 자주 사용합니다. Java에서의 이벤트 처리는 사용자 지정 수신기 클래스를 구현하여 수행하는 반면, C# 개발자는 대리자를 사용하여 이벤트를 처리할 수 있습니다. 이벤트는 이벤트 선언 앞에 키워드 event가 있다는 점을 제외하고는 대리자 형식의 필드와 마찬가지로 선언됩니다. 이벤트는 일반적으로 public으로 선언되지만 모든 액세스 가능성 한정자를 사용할 수 있습니다. 다음 예제에서는 delegate 및 event의 선언을 보여 줍니다.

// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);

// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;

이벤트 대리자는 멀티캐스트로서, 하나 이상의 이벤트 처리 메서드에 대한 참조를 가질 수 있습니다. 대리자는 이벤트에 대해 등록된 이벤트 처리기 목록을 유지하여 이벤트를 발생시킨 클래스의 발송자 역할을 합니다. 다음 예제에서는 여러 함수에서 이벤트 하나를 구독하도록 하는 방법을 보여 줍니다. 클래스 EventClass에는 대리자와 이벤트 및 이벤트를 호출하기 위한 메서드가 들어 있습니다. 이벤트를 선언한 클래스 내에서만 이벤트를 호출할 수 있습니다. 클래스 TestEvents에서는 이벤트 구독에 += 연산자를 사용하고 구독 취소에 -= 연산자를 사용합니다. InvokeEvent 메서드를 호출하면 다음 예제에서와 같이 이벤트가 발생하고 이 이벤트를 구독하는 모든 함수가 동시에 실행됩니다.

public class EventClass
{
    // Declare the delegate type:
    public delegate void CustomEventHandler(object sender, System.EventArgs e);

    // Declare the event variable using the delegate type:
    public event CustomEventHandler CustomEvent;

    public void InvokeEvent()
    {
        // Invoke the event from within the class that declared the event:
        CustomEvent(this, System.EventArgs.Empty);
    }
}

class TestEvents
{
    private static void CodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("CodeToRun is executing");
    }

    private static void MoreCodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("MoreCodeToRun is executing");
    }

    static void Main()
    {
        EventClass ec = new EventClass();

        ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
        ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun); 

        System.Console.WriteLine("First Invocation:");
        ec.InvokeEvent();

        ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);

        System.Console.WriteLine("\nSecond Invocation:");
        ec.InvokeEvent();
    }
}

출력

First Invocation:

CodeToRun is executing

MoreCodeToRun is executing

Second Invocation:

CodeToRun is executing

참고 항목

작업

대리자 샘플

개념

C# 프로그래밍 가이드

참조

대리자(C# 프로그래밍 가이드)

이벤트(C# 프로그래밍 가이드)

기타 리소스

Java 개발자를 위한 C# 프로그래밍 언어