csharp 针对C#的行动辩护人
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp 针对C#的行动辩护人相关的知识,希望对你有一定的参考价值。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Timers;
namespace DebounceUtils
{
/// <summary>
/// Event debouncer helps to prevent calling the same event handler too often (like mark Dirty or Invalidate)
/// </summary>
public static class Debouncer
{
/// <summary>
/// Configuration contract of debouncer
/// </summary>
public interface IConfig
{
/// <summary>
/// Timeout that timer will countdown
/// </summary>
TimeSpan Timeout { get; }
/// <summary>
/// Sync invoke object for timer
/// </summary>
ISynchronizeInvoke SyncronizationObjectForTimer { get; }
/// <summary>
/// Action that will be invoked when timeout passed
/// </summary>
/// <param name="config"></param>
void OnTimeout();
}
/// <summary>
/// Internal debouncer entry
/// </summary>
class DebounceRegistration : IDisposable
{
public object Key;
public IConfig Config;
//public Timer Timer;
public IDisposable TimerDisposer = null;
public void Dispose()
{
TimerDisposer?.Dispose();
DebounceRegistration foo;
activeDebouncers.TryRemove(this.Key, out foo);
}
public IDisposable Lock()
{
return new DisposeAction(a => {
Monitor.Enter(this);
a.OnDispose = () =>
{
Monitor.Exit(this);
};
});
}
public void Debounce()
{
//lock (this) //moved to static method debounce
{
if (TimerDisposer != null)
TimerDisposer.Dispose();
TimerDisposer = new DisposeAction(d => {
//TODO: consider using System.Threading.Timer instead
var timer = null as System.Timers.Timer;
if (timer == null)
{
timer = new System.Timers.Timer();
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = false;
timer.Enabled = false;
}
timer.Interval = Config.Timeout.TotalMilliseconds;
timer.SynchronizingObject = Config.SyncronizationObjectForTimer;
timer.Start();
d.OnDispose = () => {
using (this.Lock())
{
timer.Stop();
timer.Elapsed -= Timer_Elapsed;
timer.Dispose();
this.TimerDisposer = null;
}
};
});
}
}
void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
//fist we dispose debouncer, and then invoke it's action
using (this.Lock())
{
Dispose();
}
//Happening in syncronized mode already thanks to Timer.SyncronizingObject
//but if dispatcher is used, then need to sync manually
Config.OnTimeout();
}
}
/// <summary>
/// Debounce abstract key-ed object
/// </summary>
/// <typeparam name="TConfig"></typeparam>
/// <param name="key"></param>
/// <param name="createConfigIfNotExist"></param>
/// <param name="configDebounce"></param>
public static void Debounce<TConfig>(object key, Func<TConfig> createConfigIfNotExist, Action<TConfig> configDebounce)
where TConfig : IConfig
{
bool created = false;
var debouncer = activeDebouncers.GetOrAdd(key, k => {
created = true;
var config = createConfigIfNotExist();
var res = new DebounceRegistration()
{
Key = key,
Config = config
};
configDebounce(config);
return res;
});
using (debouncer.Lock())
{
if (!created)
configDebounce((TConfig)debouncer.Config);
debouncer.Debounce();
}
}
static ConcurrentDictionary<object, DebounceRegistration> activeDebouncers = new ConcurrentDictionary<object, DebounceRegistration>();
public abstract class ConfigBase : IConfig
{
public ISynchronizeInvoke SyncronizationObject { get; set; }
ISynchronizeInvoke IConfig.SyncronizationObjectForTimer
{
get
{
return SyncronizationObject;
}
}
public Dispatcher SyncronizationDispatcher { get; set; }
public TimeSpan Timeout { get; set; } = TimeSpan.FromMilliseconds(500);
void IConfig.OnTimeout()
{
OnTimeout();
}
protected void OnTimeout()
{
Action doInvoke = InvokeOnTimeout;
if (SyncronizationDispatcher != null)
SyncronizationDispatcher.BeginInvoke(doInvoke, null);
else
doInvoke();
}
protected abstract void InvokeOnTimeout();
}
/// <summary>
/// Standard action debounce config
/// </summary>
public class ActionConfig : ConfigBase
{
public new Action<ActionConfig> OnTimeout;
public object Data;
protected override void InvokeOnTimeout()
{
OnTimeout?.Invoke(this);
}
}
public static void DebounceAction(object key, Action<ActionConfig> actionOnTimeout, Dispatcher sync, TimeSpan ? timeout = null)
{
DebounceActionCustom(key, d => {
d.OnTimeout = actionOnTimeout;
d.SyncronizationObject = null;
d.SyncronizationDispatcher = sync;
if (timeout.HasValue)
d.Timeout = timeout.Value;
});
}
public static void DebounceAction(object key, Action<ActionConfig> actionOnTimeout, TimeSpan ? timeout=null, ISynchronizeInvoke sync=null)
{
DebounceActionCustom(key, d => {
d.OnTimeout = actionOnTimeout;
d.SyncronizationObject = sync;
d.SyncronizationDispatcher = null;
if (timeout.HasValue)
d.Timeout = timeout.Value;
});
}
public static void DebounceActionCustom(object key, Action<ActionConfig> configDebounce)
{
Debounce(key, ()=>new ActionConfig(), configDebounce);
}
public class DebounceQueueConfig<T> : ConfigBase
{
/// <summary>
/// Does not have to be thread-safe, because it's already thread-safe due to DebounceRegistration
/// </summary>
public List<T> Queue { get; } = new List<T>();
public new Action<DebounceQueueConfig<T>> OnTimeout;
protected override void InvokeOnTimeout()
{
OnTimeout(this);
}
}
/// <summary>
/// Enqueue item to debounce
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="itemToEnqueue"></param>
/// <param name="actionOnQueueOnTimeout"></param>
/// <param name="timeout"></param>
/// <param name="dispatcher"></param>
/// <param name="syncObj"></param>
public static void DebounceQueue<T>(object key, T itemToEnqueue, Action<DebounceQueueConfig<T>> actionOnQueueOnTimeout, TimeSpan? timeout = null, Dispatcher dispatcher = null, ISynchronizeInvoke syncObj=null)
{
Debounce(key, () => new DebounceQueueConfig<T>(), config => {
config.Queue.Add(itemToEnqueue);
config.OnTimeout = actionOnQueueOnTimeout;
if (timeout.HasValue) config.Timeout = timeout.Value;
config.SyncronizationDispatcher = dispatcher;
});
}
}
}
以上是关于csharp 针对C#的行动辩护人的主要内容,如果未能解决你的问题,请参考以下文章
VS Code的Issue 列表被黑产“攻陷”;红帽为杀死 CentOS 发行版辩护;阿里之后腾讯可能是下一个 | 架构视点