从 StackFrame 获取类型 T

发布于 2024-12-07 07:37:18 字数 2244 浏览 0 评论 0原文

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

问题是,当从泛型调用时,StackFrame 似乎只包含开放定义类型参数,而不是封闭定义类型参数。如何从 StackFrame 获取类型参数?类似于这个问题。我想我的情况有所不同,因为 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();
        }
    }
}

The goal is to create a generic instance based on the type that called my method.

The problem is that when called from a generic, the StackFrame only appears to contain the open definition type parameters instead of the closed definition type arguments. How do I get type arguments from a StackFrame? Similar to this question. I'd like to think my situation is different since Log.Debug is being called from a closed method.

If StackFrame isn't the right approach, any suggestions other than IoC? This code is meant to fill in the case where a reference to my Unity container isn't available.

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();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

撩发小公举 2024-12-14 07:37:18

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

但更一般地说,您的问题表明了对堆栈框架告诉您的内容的根本误解。你说

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

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

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

但我们不需要那么具有异国情调。例如,假设方法 M 包含对方法 N 的调用,而 N 调用方法 O。如果抖动选择将 N 内联到 M 内部,则从 O 观察到的堆栈帧将不包含 N。堆栈帧告诉您控制权在哪里当当前方法返回时继续。当 O 返回时,控制将在 M 内部恢复,而不是 N,因此堆栈帧不包含有关 N 的任何信息。

The stack frame is not reliable and is for debugging purposes only. You cannot assume that anything useful is there. That's why it is in the "Diagnostics" namespace.

More generally though, your question demonstrates a fundamental misunderstanding about what the stack frame tells you. You said

The goal is to create a generic instance based on the type that called my method.

The stack frame does not actually tell you who called your method. The stack frame tells you where control is going to return to. The stack frame is the reification of continuation. The fact that who called the method and where control will return to are almost always the same thing is the source of your confusion, but I assure you that they need not be the same.

In particular, the coming "async/await" feature currently in preview release demonstrates the truth of this. Code that resumes from an await has no clue on the stack frame about who called it originally; that information is lost forever. Since the code that is going to run next is logically decoupled from the code that called the method originally, the stack frame does not contain that information.

We need not get as exotic as that though. For example, suppose a method M contains a call to a method N, and N calls a method O. If the jitter chooses to inline N inside M then the stack frame observed from O will not contain N. The stack frame tells you where control resumes when the current method returns. Control will resume inside M when O returns, not N, so the stack frame does not include any information about N.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文