【问题标题】:Getting Type T from a StackFrame从 StackFrame 获取类型 T
【发布时间】:2011-09-28 16:03:48
【问题描述】:

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

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

【问题讨论】:

    标签: c# .net generics reflection


    【解决方案1】:

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

    不过,更一般地说,您的问题表明对堆栈框架告诉您的内容存在根本性的误解。你说

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

    堆栈帧实际上并没有告诉你谁调用了你的方法。堆栈帧告诉您控制权将返回到哪里栈帧是延续的具体化谁调用了方法控制权将返回到哪里几乎总是相同的事实是你困惑的根源,但我向你保证,它们不一定是相同的。

    特别是,目前预览版中即将推出的“async/await”功能证明了这一点。从等待中恢复的代码在堆栈帧上没有关于最初是谁调用它的线索;该信息将永远丢失。由于接下来要运行的代码在逻辑上与最初调用该方法的代码是解耦的,因此堆栈帧不包含该信息。

    不过,我们不必变得那么奇特。例如,假设方法 M 包含对方法 N 的调用,而 N 调用方法 O。如果抖动选择在 M 中内联 N,则从 O 观察到的堆栈帧将不包含 N。堆栈帧告诉您控制在哪里当当前方法返回时恢复。当 O 返回时,控制将在 M 内部恢复,而不是 N,因此堆栈帧不包含任何有关 N 的信息。

    【讨论】:

    • 实际上,System.Diagnostics 中有很多东西有用的,比如 Process 类(例如 Process.Start)。所以命名空间不会传达不可靠性。您只需要了解诸如内联之类会使堆栈框架不可靠的事情。
    • 很好的说明。看来不仅我使用 StackFrame 的方法,而且“知道谁给你打电话”的整个概念在 .NET 中都是错误的想法。
    • 在 C# vNext 中,我们期望获得CallerFilePathCallerLineNumberCallerMemberName 参数属性,这在这种情况下可能很有用。
    • @StephenCleary:好点。而那些将(通常)由 compiler 填充,它比 runtime 更了解这些事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-24
    相关资源
    最近更新 更多