끝나지 않는 프로그래밍 일기


1. 확장 메소드(Extension Method)

확장 메소드는 지금까지 우리가 보아왔던 메소드와는 다르게 조금 독특한 메소드라고 할 수 있습니다. 이 확장 메소드는, 기존 클래스의 기능을 확장시켜주는 메소드라고 볼 수 있습니다. 아래는 확장 메소드의 선언 형식입니다.

namespace 네임스페이스명
{
    public static class 클래스명
    {
        public static 반환형식 메소드명(this 확장대상형식 식별자, 매개변수..)
        {
            ..
        }
        ..
    }
}

선언 형식을 보시면, 정적(static) 클래스를 먼저 정의하고 그 안에 확장 메소드가 정의되었습니다. 확장 메소드 역시 정적(static) 메소드여야 합니다(정적 메소드는 객체를 만들지 않고도 바로 호출이 가능). 그리고 메소드의 첫번째 매개변수에서 this 한정자가 존재해야 합니다. 바로 확장 메소드와 관련된 예제를 살펴보도록 합시다. 

using System;
using Extension;

namespace Extension
{
    public static class ExtensionMethod
    {
        public static int Multiplication(this int var, int a, int b)
        {
            int result = var;

            for (int i = 0; i < b; i++)
                result *= a;

            return result;
        }
    }
}

namespace Example
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}", 5.Multiplication(2, 3));
        }
    }
}

결과:

40

계속하려면 아무 키나 누르십시오 . . .


코드를 보시면, 6행에서 확장 메소드인 Multiplication()을 포함하는 정적 클래스인 ExtensionMethod가 보입니다. 이 메소드가 어떻게 사용되는지는 22행을 보면 알 수 있습니다. 마치 인스턴스 메소드처럼 5의 뒤에 .을 붙여 메소드를 호출하고 있습니다. 여기서 5는 Multiplication()의 매개변수 var에 들어가며, 2는 a에, 3은 b에 들어가게 됩니다. 10~15행을 살펴보면 result란 변수에 var의 값을 담아, result에 a를 b번 곱하고 이 결과값을 호출부로 반환하여, 반환된 값을 출력시킵니다.


이러한 확장 메소드는 아래와 같은 상황에서 유용하게 쓰일 수 있습니다.

이미 빌드된 라이브러리를 참조해서 사용하고 있는데, 이 라이브러리 내부에 있는 기존의 클래스를 좀 더 확장시키고 싶어. 그런데 나에게는 이 라이브러리의 소스 코드가 없기 때문에 클래스를 직접 수정할 수는 없을 것 같아.

이 경우에는 확장 메소드를 통해 기존 클래스의 소스 코드를 변경하지 않고 기능을 확장할 수 있습니다. 물론, 이와 같은 경우에도 우리가 앞에서 배운 상속을 통해서도 기능을 확장할 수 있습니다. 상속도 좋은 방법이지만, 클래스가 sealed로 한정되어 있는 경우에는 확장 메소드의 사용을 고려해 볼 수 있습니다.


2. 분할 클래스(Partial Class)

클래스의 구현이 길어질 경우 두 개 이상의 소스 파일로 분할하여 동시에 작업을 수행하거나, 관리의 편의를 위해 클래스를 분할하기도 합니다. 클래스를 분할하려면 partial 키워드를 사용하면 됩니다. 클래스 말고도 앞으로 배울 인터페이스, 구조체에도 partial 키워드를 사용할 수 있습니다. 아래는 분할 클래스의 예제입니다.

using System;

namespace Example
{
    partial class Nested
    {
        public void Test() { Console.WriteLine("Test()"); }
    }
    partial class Nested
    {
        public void Test2() { Console.WriteLine("Test2()"); }
    }
    partial class Nested
    {
        public void Test3() { Console.WriteLine("Test3()"); }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Nested nested = new Nested();
            nested.Test();
            nested.Test2();
            nested.Test3();
        }
    }
}

결과:

Test()

Test2()

Test3()

계속하려면 아무 키나 누르십시오 . . .


partial 키워드가 붙은 클래스는 컴파일 시 컴파일러에 의해 하나로 합쳐집니다. 분할에는 제한이 없으며, 여러 번 분할해도 상관이 없습니다.


3. 중첩 클래스(Nested Class)

중첩 클래스는 클래스 내에 또 클래스가 정의된 클래스를 말합니다. 전에 배운 중첩 for, while, if문 등과 같이 클래스도 중첩이 가능합니다. 이 중첩 클래스는 주로 외곽 클래스에서만 사용하고자 할때, 외부에 정의하는 것보다 관련있는 클래스를 내부 클래스로 두어 코드를 쉽게 이해하기 위해 사용됩니다. 참고로, 내부에 쓰인 클래스는 제한자가 명시되어 있지 않으면 private로 보호 수준이 지정됩니다. 즉, A라는 클래스 안에 private로 지정된 B라는 클래스가 존재하면, B 클래스는 A 클래스 밖에서 보이지 않습니다. A 클래스 내에서만 사용이 가능합니다.

class 클래스명
{
    class 클래스명
    {
        ..
    }
}

아래는 중첩 클래스가 쓰인 예제입니다.

using System;

namespace ConsoleApplication21
{
    public class OuterClass
    {
        private int a = 70;

        public class InnerClass
        {
            OuterClass instance;

            public InnerClass(OuterClass a_instance)
            {
                instance = a_instance;
            }

            public void AccessVariable(int num)
            {
                this.instance.a = num;
            }

            public void ShowVariable()
            {
                Console.WriteLine("a : {0}", this.instance.a);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterClass outer = new OuterClass();
            OuterClass.InnerClass inner = new OuterClass.InnerClass(outer);

            inner.ShowVariable();
            inner.AccessVariable(60);
            inner.ShowVariable();
        }
    }
}

결과:

a : 70

a : 60

계속하려면 아무 키나 누르십시오 . . .


코드를 보시면, 13~16행에서 InnerClass의 생성자에 OuterClass의 객체를 넘겨주어 instance 객체에 OuterClass의 객체인 a_instance를 가져옵니다. 그리고 18~21행에서는 매개변수 num을 받아 num 값을 가지고 instance 객체의 멤버 변수 a의 값을 수정합니다. 여길 보아, 안쪽에 정의된 클래스에서 바깥쪽에 정의된 클래스의 private 멤버에 접근할 수 있음을 의미합니다. 그리고 23~26행은 그 instance 객체의 멤버 변수 a의 값을 출력시킵니다.