Extension method 그 최고의 유용성
C# 3.0의 새로운 기능이라 하면 Linq만을 생각 하시는 분들이 많은거 같아, 오늘은 C# 3.0의 새 기능중 내 생각에 가장 유용하다. 아니 적어도 Design 면에서는 가장 유용하다할 Extension method에 대해 써 볼까 한다.
뭐 Extension method가 무엇이냐, 이런건 인터넷에서 쉽게 찾아 볼수 있으니 자세한 설명은 생략하고 그냥 간단한 예제 하나로 대체 하도록 하겠다.
using System;
namespace Examples { public static class Extensions { // Extension method 정의 public static double GetSqrt(this double d) { return Math.Sqrt(d); } }
public class Test { public Test() { // Extension method 사용 double d = (1.1).GetSqrt(); } } } |
위 구문이 정말 가장 간단한 Extension method의 사용방법이라 하겠다. public static class 정의 후에 public static method를 정의 하고, 그 첫번째 parameter로 this type identifier 하면 되겠다.
사실 이 Extension method는 그 기능 자체만 보자면 별로 특별할것도 없고 이것이 있으므로 해서 전에는 못했던 아주 특별한 뭔가를 할수 있는것도 아니다. 어떻게 보면 이건 단지 기존에 Extensions.GetSqrt(1.1)이라 해야 했던걸 그냥 (1.1).GetSqrt()할수 있게 해주는것 밖에 없다.
하지만 기능 이외에 Design적 관점을 더 하면 말이 달라진다. Extension method의 그 유용성은 Design 면에서 최고의 빛을 발한다. 프로그래머들이 framework를 제작할때 항상 두가지의 상충된 design의 문제로 고민하게 된다. 첫번째는 framework의 심플함과 유연성이고 두번째는 framework 사용의 편리성이다.
첫번째 design 포인트는 프로그래머로 하여금 가장 최소한의 API만을 제공하고 data class와 operation class의 구분을 요구한다. 하지만 두번째 design 포인트는 보다 많은 helper method와 자주 쓰이는 logic은 그냥 framework에서 method call 하나로 알아서 다 처리 해 주길 요구한다.
문제는 이 두개의 design 포인트가 동전의 양면처럼 서로 공존 할수 없다는 점이다. 이전 까지는 항상 framework를 제작할때 이 두개를 어떻게 잘 balance 할것인가가 중요한 문제 였다.
그러나!! Extension method는 이 문제를 정말 깔끔하게 처리해 준다. C# 3.0 이상을 요구 하는 framework는 더 이상 이 balance에 대해 고민할 필요가 없다. framework 자체는 첫번째 디자인 포인트에 맞춰 많은 data class와 data manipulate에 필요한 가장 최소한의 method만을 제공하면 된다.
Design 포인트 2번째는 모두 다 Extension method를 이용해서 만족 시켜 주면 되는것이다.
using System; using System.Net.Mail; namespace Examples { public class Account { public string AccountNumber { public get; private set; } public string OwnerName { public get; private set; } public string OwnerEmail { public get; private set; } public double Balance { public get; private set; } public Account(string accountNumber, string ownerName, string ownerEmail) { this.AccountNumber = accountNumber; this.OwnerName = ownerName; this.OwnerEmail = ownerEmail; this.Balance = 0; } public double Deposit(double amount) { Balance += amount; return Balance; } public double Withdraw(double amount) { Balance -= amount; return Balance; } } public static class TransactionExtensions { public static double TransferTo(this Account src, Account dest, double amount) { src.Withdraw(amount); dest.Deposit(amount); return amount; } public static void SendMessage(this Account to, string title, string message) { MailMessage message = new MailMessage("me@home", to.OwnerEmail, title, message); SmtpClient client = new SmtpClient(); client.Send(message); } } class Test { static void Test() { Account c = new Account("00001", "you", "you@home"); c.SendMessage("Send Email", "Test Message"); Account dest = new Account("00002", "someone", "someone@home"); c.TransferTo(dest, 1000); } } }
|
위에 예제는 작동하는건 아니고, 그냥 point만 보여 주기 위한 코드다. 위에서 처럼 Account의 내부 정보를 필요로 하는 최소한의 함수만을 class에 남겨두고 그 외의 helper class 들은 Extension method로 옮기면 된다. 이렇게 하면 실제 data class에는 최소한의 API만 남게 되며 helper method 들은 따로 한곳에 모아 둘수 있고, framework의 consumer들은 밑에 Test에서 처럼 쉽게 class들을 이용할수 있게된다.
이 외에도 여러 가지 트릭이 있을수 있는데, 이를테면 같은 데이타 클라스를 다르게 해석하게 할수 있고 (같은 signature의 extension method 지만 implementation이 다른, factory pattern의 변종?), Visitor pattern의 Visit 메서드를 실제 class가 아닌 extension methods로 만들수도 있고, 등등
하여간 C# 3.0에 새로 들어간 여러 가지 기능들이 단지 Linq만을 위한것이 아니다. 이제 C# 4.0에 들어갔는데 아마 이런 유용한 기능들이 4.0에는 더 많이 들어가지 않을까 싶다.
그럼 다음번엔 lamba expression에 대해 함 써볼까 한다. 그때까지 수고!!
Comments
- Anonymous
August 16, 2009
좋은 글 잘 읽었습니다. extension method와 프레임워크의 이상을 잘 버무려서 말씀해 주셨네요 :) 감사합니다. :)