目标是根据调用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();
}
}
}
答案 0 :(得分:17)
堆栈帧不可靠,仅用于调试目的。你不能假设有任何有用的东西。这就是它在“诊断”命名空间中的原因。
但更普遍的是,您的问题表明了对堆栈框架告诉您的内容存在根本性的误解。你说
目标是根据调用我的方法的类型创建一个通用实例。
堆栈框架实际上并没有告诉你谁调用了你的方法。堆栈帧告诉您控件将返回到的位置。 堆栈框架是延续的具体化。 调用方法和控制将返回的事实几乎总是相同的事情是你混乱的根源,但我向你保证他们不必是相同。
特别是,预览版中即将出现的“异步/等待”功能证明了这一点。从await恢复的代码在堆栈框架上没有关于谁最初调用它的线索;这些信息永远消失了。由于下一个要运行的代码在逻辑上与调用最初方法的代码分离,因此堆栈帧不包含该信息。
我们不需要像那样充满异国情调。例如,假设方法M包含对方法N的调用,并且N调用方法O.如果抖动选择在M中内联N,则从O观察到的堆栈帧将不包含N.堆栈帧告诉您控制在何处当前方法返回时恢复。当O返回时,控制将在M内部恢复,而不是N,因此堆栈帧不包含关于N的任何信息。