Unveiling Dynamic Arguments

C#과 CLR C++에서는 params 키워드, VB.NET에서는 ParamArray를 사용하여 구현할 수 있는 가변 매개 변수 배열은 참 편리한 도구입니다. 하지만 간과하기 쉬운 몇 가지 사실을 같이 숨기게 됩니다.


1. 말장난같지만 결국 배열이다.


public static object[] ToArray(params object[] args)
{
    return args;
}


위의 코드는 “말장난”입니다. 하지만 실제 사용되는 예를 보면 다음과 같습니다.


object[] test = ToArray(1, 2, 3, 4, 5, 6);


new object[] { } 대신 ToArray를 이용하여 간단히 배열을 초기화할 수 있었습니다. 이와 같이 가변 매개 변수 배열에 지정하는 모든 파라미터들은 배열로 묶여서 데이터로 지정이 되게 됩니다. 이러한 특성을 잘 이용하면 상당히 편리한 도구를 만들 수도 있는 것입니다. 이 “말장난”같은 함수가 ASP.NET과 같은 환경에서는 상당히 유용해집니다.


<%# ToArray(1, 2, 3, 4, 5, 6) %>


위와 같이 Data Assignment 표현식에서 프로그래밍 언어에 관계없이 완곡하고도 보기 쉬운 표현을 만들어낼 수 있는 것입니다. 즉, 함수를 부를 수 있는 시점이면 어떤 식으로든 사용이 가능하다는 의미입니다.


2. 가변 매개 변수 배열로는 원소가 0개이거나 null인 배열이 “절대” 올 수 없다?


결론부터 말하면 “아닙니다”. 어떻게 해서 가능한 일일까요?


object[] test = ToArray((object[])null);


컴파일러가 해석하기 나름인 구문이 될 수 있겠으나, Visual C# 컴파일러의 경우 이 구문을 해석할 때, params 키워드로 지정하게 될 가변 매개 변수 항목 전체에 대한 “대체 구문”으로 인지를 하게 됩니다. 그래서 ToArray 입장에서는 전달되는 매개변수 배열 전체가 null 참조로 지정됩니다. 그렇다면 new object[] { } 같은 구문이나 이와 동일한 상태로 설정된 개체가 오는 경우에도 전체 배열이 대치될까요? 이 경우에도 같습니다.


그렇다면 이들을 배열의 원소로 포함을 시키려면 어떻게 해야 할까요? 답은 가변 매개 변수 배열의 자식 원소 형식으로 캐스팅하여 지정하는 방법입니다. 즉, null의 경우 (object)null로, 배열의 경우 (object)new object[] { }와 같습니다.


3. 리플렉션을 이용하여 접근하는 경우는 어떻게 되나?


그냥 가져다 쓰기에도 이렇게 복잡한 사실들을 많이 내포하고 있는데 리플렉션에서는 어떨까 싶습니다. 다행스럽게도, 리플렉션을 이용하여 접근하는 입장에서는 params 키워드와는 관계없이 일반 배열 개체를 대신 지정함으로서 이런 혼란을 사전에 피할 수 있습니다. 2번 토픽에서 보았던 특징을 그대로 반영하는 것입니다. 🙂


Bouns: 실험을 위하여 사용한 코드 조각


using System;


static class Program
{
 static object[] ToArray(params object[] args)
 {
  return args;
 }


 static void Main()
 {
  object[] test = ToArray(1, 2, 3, 4, 5);
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), (test.Length > 0));


  test = ToArray(new object[] { });
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), (test.Length > 0));


  object[] temp = new object[] { };
  test = ToArray(temp);
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), (test.Length > 0));


  test = ToArray((object)temp);
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), (test.Length > 0));


  test = ToArray((object)null);
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), (test.Length > 0));


  test = ToArray((object[])null);
  Console.WriteLine(“test != null: {0}, test.Length > 0: {1}”, (test != null), “N/A”);
 }
}


댓글 남기기