从StackFrame获取Type T.

时间:2011-09-28 16:03:48

标签: c# .net generics reflection

目标是根据调用my方法的类型创建一个通用实例。

问题在于,当从泛型调用时,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();
        }
    }
}

1 个答案:

答案 0 :(得分:17)

堆栈帧不可靠,仅用于调试目的。你不能假设有任何有用的东西。这就是它在“诊断”命名空间中的原因。

但更普遍的是,您的问题表明了对堆栈框架告诉您的内容存在根本性的误解。你说

  

目标是根据调用我的方法的类型创建一个通用实例。

堆栈框架实际上并没有告诉你谁调用了你的方法。堆栈帧告诉您控件将返回到的位置。 堆栈框架是延续的具体化调用方法控制将返回的事实几乎总是相同的事情是你混乱的根源,但我向你保证他们不必是相同。

特别是,预览版中即将出现的“异步/等待”功能证明了这一点。从await恢复的代码在堆栈框架上没有关于谁最初调用它的线索;这些信息永远消失了。由于下一个要运行的代码在逻辑上与调用最初方法的代码分离,因此堆栈帧不包含该信息。

我们不需要像那样充满异国情调。例如,假设方法M包含对方法N的调用,并且N调用方法O.如果抖动选择在M中内联N,则从O观察到的堆栈帧将不包含N.堆栈帧告诉您控制在何处当前方法返回时恢复。当O返回时,控制将在M内部恢复,而不是N,因此堆栈帧不包含关于N的任何信息。