2011-09-28 5 views
3

목표는 my 메서드를 호출 한 형식을 기반으로 일반 인스턴스를 만드는 것입니다.StackFrame에서 Type T 가져 오기

제네릭에서 호출 할 때 StackFrame은 닫힌 정의 형식 인수 대신 열린 정의 형식 매개 변수 만 포함하는 것으로 나타납니다. StackFrame에서 형식 인수를 얻으려면 어떻게해야합니까? this question와 유사합니다. Log.Debug가 닫힌 메소드에서 호출되기 때문에 내 상황이 다르다고 생각하고 싶습니다.

StackFrame이 올바른 방법이 아닌 경우 IoC 이외의 제안이 있습니까? 이 코드는 Unity 컨테이너에 대한 참조를 사용할 수없는 경우를 채우기위한 것입니다.

using System; 
using System.Reflection; 

namespace ReflectionTest 
{ 
    public class Logger 
    { 
     private readonly string loggerName; 
     protected Logger(string loggerName) { this.loggerName = loggerName; } 
     public void Debug(string message) { Console.WriteLine(string.Format("{0} - {1}", loggerName, message)); } 
    } 

    public class Logger<T> : Logger 
    { 
     public Logger() : base(typeof(T).FullName) { } 
    } 

    public static class Log 
    { 
     public static void Debug(string message) 
     { 
      // Determine the calling function, and create a Logger<T> for it. 
      System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(1); 
      MethodBase method = frame.GetMethod(); 

      /// When method is from a generic class, 
      /// the method.ReflectedType definintion is open: Type.ContainsGenericParameters is true 
      /// How do I get the generic parameters of method.ReflectedType so 
      /// Activator.CreateInstance() will not throw? 
      Type logType = typeof(Logger<>); 
      Type constructed = logType.MakeGenericType(new Type[] { method.ReflectedType }); 

      Logger logger = (Logger)Activator.CreateInstance(constructed); 

      logger.Debug(message); 
     } 
    } 

    public class MyBase<T> 
    { 
     public void Run() 
     { 
      Log.Debug("Run Generic"); // throws on Activator.CreateInstance() 
     } 
    } 

    class Program 
    { 
     static void Works() 
     { 
      Log.Debug("Run NonGeneric"); // works 
     } 

     static void DoesNotWork() 
     { 
      MyBase<int> b = new MyBase<int>(); 
      b.Run(); 
     } 

     static void Main(string[] args) 
     { 
      Works(); 
      DoesNotWork(); 
     } 
    } 
} 

답변

17

스택 프레임은 신뢰할 수 없으며 디버깅 용도로만 사용됩니다. 당신은 유용하다고 생각할 수 없습니다. 그것이 "진단"네임 스페이스에있는 이유입니다.

더 일반적으로, 귀하의 질문은 스택 프레임이 귀하에게 말하는 것에 대한 근본적인 오해를 보여줍니다. 당신은

목표는 내 방법라고하는 유형에 따라 일반 인스턴스를 생성하는 것입니다 말했다.

스택 프레임은 실제로 메서드를 호출 한 을 말하지 않습니다. 스택 프레임은 제어가으로 돌아갈 을 알려줍니다. 스택 프레임은 연속의 구체화입니다.. 사실이라는 컨트롤을으로 반환하는 메서드를 호출 한 은 혼동의 근원과 거의 같은 것이지만, 동일하지 않아도된다는 것을 확신합니다.

특히 현재 출시 예정인 "async/await"기능은 현재이 기능의 진실성을 보여줍니다. 기다려온 상태에서 다시 시작하는 코드는 스택 프레임에 누가 원래 호출했는지에 대한 단서가 없습니다. 그 정보는 영원히 없어집니다. 부터 다음에 실행될 코드은 원래 메서드를 호출 한 코드 인 과 논리적으로 분리됩니다. 스택 프레임에는 해당 정보가 들어 있지 않습니다.

우리는 그처럼 이국적으로 변할 필요가 없습니다. 예를 들어, 메소드 M이 메소드 N에 대한 호출을 포함하고 N이 메소드 O를 호출한다고 가정하십시오. 지터가 M에서 N을 인라인으로 선택하면 O에서 관찰 된 스택 프레임에는 N이 포함되지 않습니다. 스택 프레임은 제어 현재 메서드가 반환 될 때 다시 시작됩니다. 컨트롤은 N이 아닌 O를 반환 할 때 M 내부에서 다시 시작합니다. 따라서 스택 프레임에 N에 대한 정보가 포함되지 않습니다.

+5

사실 System.Diagnostics에는 * Process 클래스처럼 유용합니다. Process.Start). 그래서 네임 스페이스는 신뢰할 수없는 것을 전달하지 않습니다. 스택 프레임을 신뢰할 수 없도록 만드는 인라인과 같은 것에 대해 알아야합니다. –

+0

위대한 설명. StackFrame을 사용하는 나의 방법뿐만 아니라 "누가 당신을 호출했는지"라는 전체 개념이 .NET에서의 잘못된 사고라고 생각됩니다. – ErnieL

+0

C# vNext에서 우리는이 상황에서 유용 할 수있는'CallerFilePath','CallerLineNumber','CallerMemberName' 매개 변수 속성을 얻을 것으로 예상합니다. –