우리는 C# 3.0 에 확장 메서드(Extension Methods) 가 언어적으로 지원된다는 말은 수없이도 들어보았다. 확장 메서드는 기존 C# 2.0 에 비해 굉장히 파격적이다. 추후 확장 프로퍼티와 확장 이벤트 등도 지원된다고 하니, 가히 언어적으로 파격적이다.
 
 
확장 메서드의 문제점
 
확장 메서드는 굉장히 파격적이다. 내가 C# 2.0 을 하면서 감히 이런 언어적 지원이 가능하리라곤 상상도 못했으니 그 아이디어 적이 면만으로도 충분히 놀랄만 하다.
확장 메서드는 원본 타입의 Type 에 따라 지원된다.
하지만, 자주 사용되는 string, int, bool 등과 같은 타입에 확장 메서드를 추가하게 되고, 프레임웍이 커짐에 따라 이런 확장 메서드의 양도 무한대(?)로 커질 가능성이 충분히 있다.
 
확장 메서드는 기본적으로 자신이 속한 namespace 에 한해 Intelisense 에 표시된다. 즉, 다른 namespace 의 확장 메서드를 사용하고 싶다면 using 문을 추가하면 된다.
바로, 여기에서 문제점이 도출된다.
기존 Utility 성격의 메서드나 해당 namespace 에 존재하는 많은 메서드들이 확장 메서드로 마이그레이션이 가능하다는 것이다.
C# 에서는 수많은 namespace 가 존재하고, 수많은 클래스가 존재한다. 만약, using 문을 추가해 다른 namespace 의 확장메서드를 사용하려 했으나, 중복되는 경우도 생길것이며, 구현하려는 기능이 일반 static 메서드였는지, 확장 메서드였는지 조차 기억하기엔 너무나도 번거로운 작업이 될 것이다.
 
일반적인 확정 메서드가 구현되는 상황을 보도록 하자.
 
class Program
    {
        static void Main(string[] args)
        {
                     string encrypt = "AAA".Encrypt();
 
                     Console.WriteLine(encrypt);
        }
    }
 
static class MyExtender
{
           public static string Encrypt(this string arg)
           {
                     return arg + "는 암호화 됨.(MyExtender)";
           }
}
 
Encrypt 메서드는 특정 문자열을 암호화 하는 확장 메서드이다. (암호화 코드가 있다는 가정임)
 
즉, 해당 클래스가 속해있는 namespace 에 한해 확장 메서드를 구현하고, 만약 다른 namespace 의 확장 메서드가 필요하게 되면 using 네임스페이스; 구문을 추가하여 다른 namespace 영역의 확장 메서드를 끌어다 쓰면 되는 것이다.
 
만약, using 문이 추가되면 될수록 우리가 기억 해야할 확장 메서드의 양은 얼마만큼 많아 지게 될지 알 수 없다.
 
수많은 고민을 통해 “모든 확장 메서드는 하나의 namespace 안에서 관리하자” 라는 생각을 해보았다. 즉, 확장 메서드가 필요하면 using 구문 하나만으로 모든 확장 메서드를 호출할 수 있도록 하는 구성이다.
하지만, 여기에 결정적인 단점이 있다. 이 부분은 뒷부분에 다시 설명 하도록 하겠다.
 
 
 
확장 메서드(Extension Methods) 설계
 
 
위 그림과 같이 확장 메서드를 지원하는 별도의 폴더와 namespace 를 만들었다.
이 namespace 는 솔루션 또는 프레임웍이 사용하는 모든 확장 메서드를 한군데에 모아 놓기 위한 namespace 이다.
 
즉,
 
using Umc.Core.Extender;
 
구문 하나만으로 모든 확장 메서드를 활용할 수 있다.
 
그럼 SecurityExtender.cs 파일과 TextExtender.cs 파일을 연속으로 보도록 하자.
본 소스는 예제용으로 제공되는 소스이니 크게 비중을 두지 말고 보도록 하자.
 
namespace Umc.Core.Extender
{
           public static partial class SecurityExtender
           {
                     public static Security.Security Security(this string arg)
                     {
                                return new Umc.Core.Security.Security(arg);
                     }
           }
}
 
namespace Umc.Core.Extender
{
           public static partial class TextExtender
           {
                     public static Umc.Core.Text.Text Text(this string arg)
                     {
                                return new Umc.Core.Text.Text(arg);
                     }
           }
}
 
위 두 클래스는 동일한 namespace 안에 존재하게 되며, 특히 partial 클래스로 선언되어 언제 어디서든 동일한 namespace 와 class 명을 갖게 된다면 확장 및 기능 추가가 가능하도록 했다.
 
아직 리턴 타입의 클래스 구현을 확인하진 못했지만, 어느정도 객체지향 프로그램을 해 본 사람이라면 일찌감치 감을 잡았을 것이라고 생각한다.
 
위 두 클래스가 지원하는 확장 메서드의 원본 타입은 string 이 되며, 해당 확장 메서드의 리턴 타입은 또 다른 인스턴트형의 클래스이다.
 
위처럼 구현한 이유는 확장 메서드를 쩜(.)을 찍고 구현할 수 있도록 소위 Depth(깊이)를 준 것이다. 여기에서 말하는 소위 Depth 라고 함은 다음의 그림을 보면 쉽게 이해가 갈 것이다.
 

 
위와 같이 해당 확장 메서드에서 쩜(.) 을 누르는 순간 새로운 메서드들이 Intelisense 에 나타나게 된다.
 
 
개발자 입장에서는 이렇게 직감적으로 코딩을 가능하게 하는 것도 “사용자 경험(UX)” 라고 말하고 싶다.
 
이렇게 설계가 가능하다면, 다음과 같이 최소한의 확장 메서드로 다양한 기능을 추가할 수 있다는 것이다.
 
그럼, Security 와 Text 클래스가 어떻게 구현되었는지 연속적으로 보도록 하자.
 
namespace Umc.Core.Security
{
           public class Security
           {
                     // SourceString 이 private 이므로 Object Initializer 를 사용할 수 없다.
                     public Security(string str)
                     {
                                this.SourceString = str;
                     }
 
                     // 암호화할 문자열
                     private string SourceString { get; set; }
 
                     public string Encrypt()
                     {
                                // 암호화 알고리즘이라고 가정.
                                return SourceString + "는 암호화 됨.";
                     }
 
                     public string Decrypt()
                     {
                                // 복호화 알고리즘이라고 가정.
                                return SourceString + "는 복호화 됨.";
                     }
           }
}
 
namespace Umc.Core.Text
{
           public class Text
           {
                     public Text(string str)
                     {
                                this.SourceString = str;
                     }
 
                     private string SourceString { get; set; }
 
                     public string Left(int len)
                     {
                                return SourceString.Substring(0, len);
                     }
 
                     public string Right(int len)
                     {
                                return SourceString.Substring(SourceString.Length - len , len);
                     }
 
                     // 기존 확장메서드 방식으로 인스턴스를 리턴하여 호출할 방법이 없음.
                     public static string Test()
                     {
                                return string.Empty;
                     }
           }
}
 
 
 
특히나 interface 를 통한 확장 메서드의 구현은 그 진가를 십분 발휘하게 될 것임이 분명하다.
그렇지만, 이러한 편리한 설계임에도 불구하고 단점이 존재하게 된다.
 
 
확장 메서드 설계의 문제점
 
바로 static 메서드로 구현된 메서드는 호출할 뾰족한 방법이 없다.
위의 확장 메서드의 설계 에서 보듯이, 구현된 클래스는 반드시 인스턴스를 생성하여 사용할 수 있도록 구성되었다.
 
아래와 같이
 
 
처럼 static 으로 구현된 메서드는 인스턴스를 생성하여도 호출할 방법이 없다.
우리가 설계한 확장 메서드는 생성자에 인자값을 전달하는 형식이지만, 더군다나 static 메서드는 생성자에 인자값을 전달한다고 하더라도, 생성자가 전달한 인자값을 활용할 방법이 없다는 것이다. 아마도 이 부분은 확장 프로퍼티라는 개념이 C# 3.0 정식 버젼에서 지원하느냐, 어떻게 지원하느냐가 관건이 될 것 같다.
 
 
마치며
 
C# 3.0 의 Lambda, LINQ, Extension Methods 는 새로 등장하는 시스템의 설계에 엄청난 파장을 불러 일으킬만한 이슈이다. 아직 완벽하게 완성된 C# 3.0 은 아니지만, 분명한 것은 수많은 Language(언어) 들의 표본이 C# 3.0 이 될 것이 분명하다.(이렇게 말하니 무슨 홍보대사 같네 ㅋ)
정식 C# 3.0 의 모습을 기대하면서 이만 ^^//
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 땡초 POWERUMC

댓글을 달아 주세요