Obtendo o tipo T de um StackFrame

O objective é criar uma instância genérica com base no tipo que chamou meu método.

O problema é que, quando chamado de um genérico, o StackFrame parece conter apenas os parâmetros do tipo de definição aberta, em vez dos argumentos de tipo de definição fechados. Como obtenho argumentos de tipo de um StackFrame? Semelhante a esta pergunta . Eu gostaria de pensar que minha situação é diferente desde Log.Debug está sendo chamado de um método fechado.

Se o StackFrame não é a abordagem correta, alguma sugestão além do IoC? Esse código serve para preencher o caso em que uma referência ao meu contêiner Unity não está disponível.

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 : 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 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 { 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 b = new MyBase(); b.Run(); } static void Main(string[] args) { Works(); DoesNotWork(); } } } 

   

O quadro de pilha não é confiável e é apenas para fins de debugging. Você não pode presumir que alguma coisa útil esteja lá. É por isso que está no namespace “Diagnóstico”.

Mais geralmente, sua pergunta demonstra um mal-entendido fundamental sobre o que o quadro de pilha informa a você. Você disse

O objective é criar uma instância genérica com base no tipo que chamou meu método .

O quadro da pilha não diz quem chamou seu método . O quadro de pilha informa onde o controle retornará . O quadro da pilha é a reificação da continuação . O fato de quem chamou o método e para o qual o controle retornará é quase sempre a mesma coisa é a fonte da sua confusão, mas asseguro que eles não precisam ser os mesmos.

Em particular, o recurso “async / await” que está chegando no release de pré-visualização demonstra a verdade disso. O código que retorna de um await não tem nenhum indício no frame da pilha sobre quem o chamou originalmente; essa informação é perdida para sempre. Como o código que será executado a seguir é logicamente desacoplado do código que originalmente chamava o método , o quadro da pilha não contém essa informação.

Nós não precisamos ser tão exóticos quanto isso. Por exemplo, suponha que um método M contenha uma chamada a um método N e N chame um método O. Se o jitter escolher inline N dentro de M, o quadro de pilha observado de O não conterá N. O quadro de pilha informa onde o controle continua quando o método atual retorna. O controle será retomado dentro de M quando O retornar, não N, portanto, o quadro de pilha não inclui nenhuma informação sobre N.