Если вы хотите перебор, чтобы метод вызывался из Init, вы можете проверить стек вызовов. Примерно так:
public static bool CalledFromInit()
{
//Grab the current Stack Trace and loop through each frame
foreach(var callFrame in new StackTrace().GetFrames())
{
//Get the method in which the frame is executing
var method = callFrame.GetMethod();
//Check if the method is Control.OnInit (or any other method you want to test for)
if(method.DeclaringType == typeof(Control) && method.Name == "OnInit")
//If so, return right away
return true;
}
//Otherwise, we didn't find the method in the callstack
return false;
}
Тогда вы бы использовали это как:
public static void PrependTitle(this Page page, string newTitle)
{
//If we aren't called from Init, do something
if (!CalledFromInit())
{
//We could either return to silently ignore the problem
return;
//Or we could throw an exception to let the developer know they
// did something wrong
throw new ApplicationException("Invalid call to PrependTitle");
}
//Do the normally processing
page.Title = newTitle + " " + Global.TITLE_DELIMITER + " " + page.Title;
}
Однако я бы предостерег, что трассировка стека не самая надежная вещь. В выпуске код можно оптимизировать так, чтобы метод Control.OnInit был встроен, чтобы ваш код не смог увидеть его в стеке вызовов. Вы можете заключить эту проверку в блок #if DEBUG
, чтобы она выполнялась только во время разработки. В зависимости от вашего варианта использования может оказаться достаточно уловить эту проблему, находясь в режиме отладки, и не пытаться выполнить проверку в RELEASE. Но это зависит от вас.
Другой вариант ... основанный на ответе Томми Хинрихса, если все ваши страницы наследуют от базового класса, вы сможете сделать это немного более надежно. Я бы предложил что-то вроде этого:
public abstract class BasePage : Page
{
private bool _executingInit;
protected internal override void OnPreInit(EventArgs e)
{
_executingInit = true;
base.OnPreInit(e);
}
protected internal override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
_executingInit = true;
}
public void PrependTitle(string newTitle)
{
if (!_executingInit)
throw new ApplicationException("Invalid call to PrependTitle.");
Title = newTitle + " " + Global.TITLE_DELIMITER + " " + Title;
}
}
Таким образом, PrependTitle будет генерировать исключение, если оно не вызывается между PreInit и InitComplete (что звучит именно так, как вы хотите).
Как последний вариант, вы можете быть хитрым и использовать отражение, чтобы получить доступ к свойству Control.ControlState
(которое вводит в заблуждение, поскольку оно не связано с Control State - что-то похожее на View State). Это свойство отслеживает элемент управления по ходу его жизненного цикла и имеет следующие значения:
internal enum ControlState
{
Constructed,
FrameworkInitialized,
ChildrenInitialized,
Initialized,
ViewStateLoaded,
Loaded,
PreRendered
}
Вы заметите, что Enum является внутренним. Так же и свойство Control.ControlState. Но с Reflection вы можете использовать это - и вы можете даже использовать его из метода расширения, который является внешним по отношению к странице.
Надеюсь, один из этих способов сработает для вас!