Получение Типа T из StackFrame - PullRequest
       15

Получение Типа T из StackFrame

3 голосов
/ 28 сентября 2011

Цель состоит в том, чтобы создать универсальный экземпляр на основе типа, который вызвал мой метод.

Проблема в том, что при вызове из универсального элемента 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();
        }
    }
}

1 Ответ

17 голосов
/ 28 сентября 2011

Кадр стека ненадежен и предназначен только для целей отладки. Вы не можете предполагать, что здесь есть что-то полезное. Вот почему он находится в пространстве имен «Диагностика».

В более общем смысле, ваш вопрос демонстрирует фундаментальное недопонимание того, что говорит вам фрейм стека. Вы сказали

Цель состоит в том, чтобы создать универсальный экземпляр на основе типа, который назвал мой метод .

Кадр стека фактически не сообщает вам , кто вызвал ваш метод . Кадр стека сообщает вам , где управление вернется к . Фрейм стека является реализацией продолжения . Тот факт, что , который вызвал методы и , где контроль вернется к , почти всегда одно и то же, является источником вашего замешательства, но я вас уверяю, что они не обязательно должны быть одинаковыми.

В частности, новая функция «async / await», которая в настоящее время находится в предварительном выпуске, демонстрирует истинность этого. Код, который возобновляется с ожидающего, не имеет никакого понятия в кадре стека о том, кто вызвал его первоначально; эта информация потеряна навсегда. Поскольку код, который будет выполняться следующим , логически отделен от кода, который первоначально вызвал метод , кадр стека не содержит эту информацию.

Нам не нужно быть таким экзотическим, как это. Например, предположим, что метод M содержит вызов метода N, а N вызывает метод O. Если джиттер выбирает встроенный N внутри M, то кадр стека, наблюдаемый из O, не будет содержать N. Кадр стека говорит вам, где управление возобновляется, когда текущий метод возвращается. Когда O вернется, управление возобновится внутри M, а не N, поэтому кадр стека не содержит никакой информации о N.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...