Использование StackTrace для определения вызывающего метода

Обновлено: Предупреждение. Теперь я понимаю, что следующий метод обычно считается плохой идеей, потому что он создает скрытые зависимости, чтобы выглядеть аккуратно.


Недавно я обнаружил, что вы можете использовать StackTrace для вывода информации о вызывающем методе.

Это позволяет вам создать, казалось бы, «крутой» API, посредством которого вы просто вызываете метод, не беспокоясь о передаче ему каких-либо явных параметров, и метод определяет, что делать, на основе StackTrace.

Это плохо, и если да, то почему?

Пример:

public class Cache {
  public Object CheckCache()
  {
    Object valueToReturn = null;
    string key = GenerateCacheKeyFromMethodPrototype(new StackTrace().GetFrame(1).GetMethod()); //frame 1 contains caller
    if (key is in cache) valueToReturn = itemFromCache;

    return valueToReturn;   
  }
}

public class Foo { 
  private static Cache cache = new Cache();

  public Blah MethodFoo(param1, param2...)
  {
    Blah valueToReturn = cache.CheckCache(); //seems cool!
    if (valueToReturn == null)
    {
      valueToReturn = result of some calculation;
      //populate cache
    }

    return valueToReturn;
  }
}

Я уверен, что в приведенном выше псевдокоде есть ошибки, но вы меня поняли.


Обновлено: спасибо за все ответы.

У меня был аналогичный вопрос, так как я хотел выполнить код, условно основанный на вызывающем методе: stackoverflow.com/questions/213192/…. Компромиссы того не стоили, ИМО

Jeremy Frey 25.11.2008 21:19
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
912
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Есть две причины, по которым этого не делать:

  • Это медленно
  • Это создает хрупкое решение.

Если вы хотите сделать это, вам лучше использовать инструмент, поддерживающий аспектно-ориентированное программирование, например динамический прокси Castle.

спасибо за ответ - пожалуйста, не могли бы вы подробнее объяснить, почему это хрупкое?

Ben Aston 25.11.2008 21:19

Что происходит при удалении вызывающего метода? В вашем примере это не так важно, но в других, более реальных случаях это может иметь значение. Кроме того, что происходит, когда есть перегруженные методы? Будет ли эта техника проверки стека, чтобы решить, что делать, по-прежнему правильно?

Mike Chess 25.11.2008 21:35

Другая проблема заключается в том, что компилятор может «встроить» ваш метод в процесс оптимизации, например

void MethodA() {
    MethodB();
}

void MethodB() {
   foo();
}

становится:

void MethodA() {
   foo();
}

Это, очевидно, проблема, потому что непосредственно вызывающий foo не является MethodB, а является MethodA. Однако есть атрибут, который вы можете поместить в свой метод, чтобы предотвратить его встраивание:

[MethodImpl( ... NoInline )]

(Не могу вспомнить точный параметр)

-Oisin

[MethodImpl (MethodImplOptions.NoInlining)] - это параметр. Хотя, как говорили другие, это должен быть последний вариант. Я нашел только половину правильного использования для этого за столько лет программирования.

Chuck 08.10.2010 00:11

Это плохо, потому что я не могу знать, что делает функция, когда я ее вызываю. Я не могу протестировать функцию, потому что мой тест вызовет ее из другой функции, что может вызвать другое поведение.

И это плохо, потому что теперь есть «невидимый» контракт, которому я должен подчиняться при вызове функции. Я не только должен убедиться, что переданы правильные параметры, я также должен убедиться, что текущая функция имеет правильное имя. Что, если бы я заключил вызов функции в анонимную лямбда-функцию? Внезапно я изменил поведение программы, и нет догадался об этом заранее, так что теперь я могу потратить следующий день на отладку, почему программа внезапно и волшебным образом сломалась.

Что происходит с перегруженными функциями? Вы их различаете? Функции с одинаковым именем, но в разных классах? Функции со «особыми» именами, такие как конструктор, финализатор, операторы или лямбды?

А как насчет того, чтобы функции были встроены компилятором?

Мне это совсем не кажется крутым. Зачем вам включать все возможные способы получения требуемого состояния в самом методе? Это похоже на антишаблон для внедрения зависимостей. Не ходи туда.

хорошо, спасибо за ваш ответ. Теперь я вижу, что такой подход влечет за собой целый ряд проблем.

Ben Aston 25.11.2008 22:48

Другие вопросы по теме