Если имена всех заполнителей названы, специальный словарь может использоваться для перехвата того, какие ключи пытались получить доступ и регистрироваться в массиве.
def format_keys(str_):
class HelperDict(dict):
def __init__(self):
self._keys = []
def __getitem__(self, key):
self._keys.append(key)
d = HelperDict()
str_.format_map(d)
return d._keys
Обратите внимание, что если есть неназванные заполнители, IndexError будет увеличена на .format()
(индекс кортежа за пределами диапазона).
Прежде, чем прийти к Вашему вопросу, просто считав сводный бит из основной страницы вопросов, таймер был точно, что я собирался предложить.
Это выглядит довольно чистым мне. Это означает, что можно легко "отменить" отложенное событие, если Вы должны, путем отключения таймера снова, например. Это также делает все в потоке UI (но без повторной входимости), который делает жизнь немного более простой, чем другие альтернативы могли бы быть.
Возможно, Вы могли сделать метод, который создает таймер?
void onButtonClick(object sender, EventArgs e)
{
Delay(1000, (o,a) => MessageBox.Show("Test"));
}
static void Delay(int ms, EventHandler action)
{
var tmp = new Timer {Interval = ms};
tmp.Tick += new EventHandler((o, e) => tmp.Enabled = false);
tmp.Tick += action;
tmp.Enabled = true;
}
Если Вы только сделаете это для одного управления, то подход таймера будет хорошо работать. Более устойчивый подход, поддерживающий несколько средств управления и типов событий, выглядит примерно так:
class Event
{
public DateTime StartTime { get; set; }
public Action Method { get; set; }
public Event(Action method)
{
Method = method;
StartTime = DateTime.Now + TimeSpan.FromSeconds(1);
}
}
Поддерживают Queue<Event>
в Вашей форме и имеют события UI, которые должны быть отложены, добавляют их к очереди, например:
void onButtonClick( ..)
{
EventQueue.Enqueue(new Event(MethodToCall));
}
Заставляют Ваш таймер отсчитать 10 раз приблизительно в секунду, и иметь его обработчик событий Галочки похожи на это:
void onTimerTick()
{
if (EventQueue.Any() && EventQueue.First().StartTime >= DateTime.Now)
{
Event e = EventQueue.Dequeue();
e.Method;
}
}
Можно использовать:
Thread.Sleep(1000);
, Который приостановит текущий поток в течение одной секунды. Таким образом, я сделал бы это...
С наилучшими пожеланиями!
При поиске более необычного решения можно хотеть смотреть на мое Реактивное проект LINQ. Ссылка не показывает, как решить конкретную проблему, которую Вы имеете, но должно быть возможно решить во вполне изящном стиле с помощью техники, описанной там (в целом ряду с 4 статьями).
В моем решении используется System.Threading.Timer:
public static class ExecuteWithDelay
{
class TimerState
{
public Timer Timer;
}
public static Timer Do(Action action, int dueTime)
{
var state = new TimerState();
state.Timer = new Timer(o =>
{
action();
lock (o) // The locking should prevent the timer callback from trying to free the timer prior to the Timer field having been set.
{
((TimerState)o).Timer.Dispose();
}
}, state, dueTime, -1);
return state.Timer;
}
}
Для тех, кто ограничен .NET 2.0, вот еще один вариант полезного решения Bengt:
/// <summary>
/// Executes the specified method in a delayed context by utilizing
/// a temporary timer.
/// </summary>
/// <param name="millisecondsToDelay">The milliseconds to delay.</param>
/// <param name="methodToExecute">The method to execute.</param>
public static void DelayedExecute(int millisecondsToDelay, MethodInvoker methodToExecute)
{
Timer timer = new Timer();
timer.Interval = millisecondsToDelay;
timer.Tick += delegate
{
// This will be executed on a single (UI) thread, so lock is not necessary
// but multiple ticks may have been queued, so check for enabled.
if (timer.Enabled)
{
timer.Stop();
methodToExecute.Invoke();
timer.Dispose();
}
};
timer.Start();
}