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 发行版辩护;阿里之后腾讯可能是下一个 | 架构视点

辩护人邓学平发表辩护意见:留张扣扣一命!让其在监狱度过余生

流产必须要监护人签字吗

csharp MemoryStream中针对XSD的XML验证