[英]Create Event Manager (Messaging System) in Unity Using delegate and event
[英]how to create small notification application using event and delegate
我正計划創建一個用於演示的小型演示應用程序。 在這里,我有兩個用戶“ User1”和“ User2”,他們注冊以接收通知。 我想提供用戶獲取通知的時間。
所以,我的問題是,我該如何為每個訂閱者花費時間並在那時引發Notify事件,以便每個訂閱者都得到通知?
請參考打擊代碼。
namespace ConceptDemos
{
public class Notifier
{
public delegate void NotificationHandler();
public event NotificationHandler Notify;
public Notifier()
{
}
public void OnNotify()
{
if (this.Notify != null)
{
Notify();
}
}
}
public class User1
{
public void WakeMeUp()
{
Console.WriteLine("Ringing User1's Alarm");
}
}
public class User2
{
public void StartMyTV()
{
Console.WriteLine("Starting User2's TV");
}
}
class Program
{
static void Main(string[] args)
{
Notifier Notifier = new Notifier();
User1 oUser1 = new User1();
Notifier.Notify += oUser1.WakeMeUp;
User2 oUser2 = new User2();
Notifier.Notify += oUser2.StartMyTV;
Notifier.OnNotify();
Console.ReadLine();
}
}
}
根據您的描述,聽起來您想讓計時器觸發User1
和User2
上的委托。 這可以通過幾種不同的方式來完成。 我做到這一點的一種方法是使用支持同步和異步回調的自定義Timer類。
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// The Timer provides a means to start a timer with a callback that can be repeated over a set interval.
/// </summary>
/// <typeparam name="T">A generic Type</typeparam>
public sealed class Timer<T> : CancellationTokenSource, IDisposable
{
/// <summary>
/// The timer task
/// </summary>
private Task timerTask;
/// <summary>
/// How many times we have fired the timer thus far.
/// </summary>
private long fireCount = 0;
/// <summary>
/// Initializes a new instance of the <see cref="Timer{T}"/> class.
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="state">The state.</param>
public Timer(T state)
{
this.StateData = state;
}
/// <summary>
/// Gets the state data.
/// </summary>
/// <value>
/// The state data.
/// </value>
public T StateData { get; private set; }
/// <summary>
/// Gets a value indicating whether the engine timer is currently running.
/// </summary>
/// <value>
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
/// </value>
public bool IsRunning { get; private set; }
/// <summary>
/// Starts the specified start delay.
/// </summary>
/// <param name="startDelay">The start delay in milliseconds.</param>
/// <param name="interval">The interval in milliseconds.</param>
/// <param name="numberOfFires">Specifies the number of times to invoke the timer callback when the interval is reached. Set to 0 for infinite.</param>
public void Start(double startDelay, double interval, int numberOfFires, Action<T, Timer<T>> callback)
{
this.IsRunning = true;
this.timerTask = Task
.Delay(TimeSpan.FromMilliseconds(startDelay), this.Token)
.ContinueWith(
(task, state) => RunTimer(task, (Tuple<Action<T, EngineTimer<T>>, T>)state, interval, numberOfFires),
Tuple.Create(callback, this.StateData),
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
/// <summary>
/// Starts the specified start delay.
/// </summary>
/// <param name="startDelay">The start delay in milliseconds.</param>
/// <param name="interval">The interval in milliseconds.</param>
/// <param name="numberOfFires">Specifies the number of times to invoke the timer callback when the interval is reached. Set to 0 for infinite.</param>
public void StartAsync(double startDelay, double interval, int numberOfFires, Func<T, Timer<T>, Task> callback)
{
this.IsRunning = true;
this.timerTask = Task
.Delay(TimeSpan.FromMilliseconds(startDelay), this.Token)
.ContinueWith(
async (task, state) => await RunTimerAsync(task, (Tuple<Func<T, Timer<T>, Task>, T>)state, interval, numberOfFires),
Tuple.Create(callback, this.StateData),
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
/// <summary>
/// Stops the timer for this instance. It is not
/// </summary>
public void Stop()
{
if (!this.IsCancellationRequested)
{
this.Cancel();
}
this.IsRunning = false;
}
/// <summary>
/// Cancels the timer and releases the unmanaged resources used by the <see cref="T:System.Threading.CancellationTokenSource" /> class and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.IsRunning = false;
this.Cancel();
}
base.Dispose(disposing);
}
private async Task RunTimer(Task task, Tuple<Action<T, EngineTimer<T>>, T> state, double interval, int numberOfFires)
{
while (!this.IsCancellationRequested)
{
// Only increment if we are supposed to.
if (numberOfFires > 0)
{
this.fireCount++;
}
state.Item1(state.Item2, this);
await PerformTimerCancellationCheck(interval, numberOfFires);
}
}
private async Task RunTimerAsync(Task task, Tuple<Func<T, EngineTimer<T>, Task>, T> state, double interval, int numberOfFires)
{
while (!this.IsCancellationRequested)
{
// Only increment if we are supposed to.
if (numberOfFires > 0)
{
this.fireCount++;
}
await state.Item1(state.Item2, this);
await PerformTimerCancellationCheck(interval, numberOfFires);
}
}
private async Task PerformTimerCancellationCheck(double interval, int numberOfFires)
{
// If we have reached our fire count, stop. If set to 0 then we fire until manually stopped.
if (numberOfFires > 0 && this.fireCount >= numberOfFires)
{
this.Stop();
}
await Task.Delay(TimeSpan.FromMilliseconds(interval), this.Token).ConfigureAwait(false);
}
}
現在,您可以連接如下內容:
// Create a new user and timer
User1 oUser1 = new User1();
var timer = new Timer(oUser1);
// Set the time to notify the user to 5 minutes from now.
var timeToNotify = DateTime.Now.AddMinutes(5);
new TimeSpan(timeToNotify.Hour, timeToNotify.Minute, timeToNotify.Second);
// Star the timer. In 5 minutes it will invoke the WakeMeUp() method.
timer.Start(
startDelay: timeToNotify,
interval: 0,
numberOfFires: 1,
callback: (user, timer) => user.WakeMeUp());
相反,如果您想使用某種觀察者模式,請告訴我。 有一種更好的方法可以通知用戶事件已發生,並且用戶需要對此做出反應。
另一種選擇是使用發布/訂閱模式。 使用這種模式,您可以讓每個User
實例訂閱發布者可以發送的特定發布消息。
這種用法的一個例子是這樣的:
public class User1 : IDispose
{
private ISubscription subscription;
public User1(NotificationManager notificationManager)
{
this.subscription = notificationManager.Subscribe<WakeUpNotification>(
(notification, subscription) => this.WakeMeUp());
}
public void WakeMeUp()
{
Console.WriteLine("Ringing User1's Alarm");
}
public void Dispose()
{
this.subscription.Unsubscribe();
}
}
public class User2 : IDispose
{
private ISubscription subscription;
public User2(NotificationManager notificationManager)
{
this.subscription = notificationManager.Subscribe<StartMyTvNotification>(
(notification, subscription) => this.StartMyTv());
}
public void StartMyTv()
{
Console.WriteLine("Ringing User1's Alarm");
}
public void Dispose()
{
this.subscription.Unsubscribe();
}
}
var notificationManager = new NotificationManager();
var user1 = new User1(notificationManager);
var user2 = new User2(notificationManager);
notificationManager.Publish(new WakeUpNotification(true));
notificationManager.Publish(new TurnOnMyTvNotification(DateTime.Now));
user1.Dispose();
這可以與計時器結合使用,該計時器允許以不同的時間間隔發布消息。 您可以使用計時器在不同於TurnOnMyTvNotification
發布的時間發布WakeUpNotification
。
以下是發布/訂閱設置的實現。 該代碼已被很好地記錄。
/// <summary>
/// Provides a contract for Mediators to use when handling notifications between objects.
/// </summary>
public interface INotificationCenter
{
/// <summary>
/// Sets up a new handler and returns it for subscription set up.
/// </summary>
/// <typeparam name="TMessageType">An IMessage implementation that the given handler will be provided when messages are dispatched</typeparam>
/// <param name="handler">The handler used to process incoming messages.</param>
/// <returns>Returns an ISubscription that can be used to unsubscribe.</returns>
ISubscription Subscribe<TMessageType>(Action<TMessageType, ISubscription> callback, Func<TMessageType, bool> condition = null) where TMessageType : class, IMessage;
/// <summary>
/// Publishes the specified message.
/// </summary>
/// <typeparam name="TMessageType"></typeparam>
/// <param name="message">The message.</param>
void Publish<TMessageType>(TMessageType message) where TMessageType : class, IMessage;
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
/// <summary>
/// The mediator for all messaging
/// </summary>
public class NotificationManager : INotificationCenter
{
/// <summary>
/// Collection of subscribed listeners
/// </summary>
private ConcurrentDictionary<Type, List<ISubscription>> listeners =
new ConcurrentDictionary<Type, List<ISubscription>>();
/// <summary>
/// Subscribe publications for the message type specified.
/// </summary>
/// <typeparam name="TMessageType">A concrete implementation of IMessage</typeparam>
/// <returns></returns>
public ISubscription Subscribe<TMessageType>(Action<TMessageType, ISubscription> callback, Func<TMessageType, bool> condition = null) where TMessageType : class, IMessage
{
ExceptionFactory.ThrowIf(
callback == null,
() => new ArgumentNullException(nameof(callback), "Callback must not be null when subscribing"));
Type messageType = typeof(TMessageType);
// Create our key if it doesn't exist along with an empty collection as the value.
if (!listeners.ContainsKey(messageType))
{
listeners.TryAdd(messageType, new List<ISubscription>());
}
// Add our notification to our listener collection so we can publish to it later, then return it.
// TODO: Move instancing the Notification in to a Factory.
var handler = new Notification<TMessageType>();
handler.Register(callback, condition);
handler.Unsubscribing += this.Unsubscribe;
List<ISubscription> subscribers = listeners[messageType];
lock (subscribers)
{
subscribers.Add(handler);
}
return handler;
}
/// <summary>
/// Publishes the specified message to all subscribers
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="message">The message.</param>
public void Publish<T>(T message) where T : class, IMessage
{
ExceptionFactory.ThrowIf(
message == null,
() => new ArgumentNullException(nameof(message), "You can not publish a null message."));
if (!listeners.ContainsKey(typeof(T)))
{
return;
}
foreach (INotification<T> handler in listeners[typeof(T)])
{
handler.ProcessMessage(message);
}
}
/// <summary>
/// Unsubscribes the specified handler by removing their handler from our collection.
/// </summary>
/// <typeparam name="T">The message Type you want to unsubscribe from</typeparam>
/// <param name="subscription">The subscription to unsubscribe.</param>
private void Unsubscribe(NotificationArgs args)
{
// If the key doesn't exist or has an empty collection we just return.
// We will leave the key in there for future subscriptions to use.
if (!listeners.ContainsKey(args.MessageType) || listeners[args.MessageType].Count == 0)
{
return;
}
// Remove the subscription from the collection associated with the key.
List<ISubscription> subscribers = listeners[args.MessageType];
lock (subscribers)
{
subscribers.Remove(args.Subscription);
}
args.Subscription.Unsubscribing -= this.Unsubscribe;
}
}
/// <summary>
/// Provides a contract to Types wanting to subscribe to published messages
/// with conditions and a callback.
/// </summary>
public interface ISubscription
{
/// <summary>
/// Occurs when the subscription is being unsubscribed.
/// </summary>
event Action<NotificationArgs> Unsubscribing;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ISubscription"/> is active.
/// </summary>
bool IsActive { get; }
/// <summary>
/// Unsubscribes the registerd callbacks from receiving notifications.
/// </summary>
/// <param name="notificationCenter">The notification center.</param>
void Unsubscribe();
}
using System;
/// <summary>
/// Processes a subscription message.
/// </summary>
/// <typeparam name="TMessageType">The type of the message type.</typeparam>
public interface INotification<TMessageType> : ISubscription where TMessageType : class, IMessage
{
/// <summary>
/// Registers the specified action for callback when a notification is fired for T.
/// </summary>
/// <param name="callback">The message being posted along with the subscription registered to receive the post.</param>
/// <returns></returns>
void Register(
Action<TMessageType, ISubscription> callback,
Func<TMessageType, bool> condition = null);
/// <summary>
/// Processes the message, invoking the registered callbacks if their conditions are met.
/// </summary>
/// <param name="message">The message.</param>
void ProcessMessage(TMessageType message);
}
/// <summary>
/// Allows for receiving the content of a message
/// </summary>
public interface IMessage
{
/// <summary>
/// Gets the content.
/// </summary>
/// <returns>Returns the content of the message</returns>
object GetContent();
}
/// <summary>
/// Allows for receiving the content of a message
/// </summary>
/// <typeparam name="TContent">The type of the content.</typeparam>
public interface IMessage<TContent> : IMessage where TContent : class
{
/// <summary>
/// Gets the content of the message.
/// </summary>
TContent Content { get; }
}
/// <summary>
/// Provides methods for dispatching notifications to subscription handlers
/// </summary>
/// <typeparam name="TMessageType">The type of the message type.</typeparam>
public class WakeMeUpNotification : IMessage<DateTime> where TContentType : class
{
public WakeMeUpNotification(DateTime timeToWakeUp)
{
this.Content = timeToWakeUp
}
/// <summary>
/// Gets the content of the message.
/// </summary>
public DateTime Content { get; protected set; }
/// <summary>
/// Gets the content of the message.
/// </summary>
public DateTime GetContent()
{
return this.Content;
}
/// <summary>
/// Gets the content.
/// </summary>
object IMessage.GetContent()
{
return this.GetContent();
}
}
/// <summary>
/// Provides methods for dispatching notifications to subscription handlers
/// </summary>
/// <typeparam name="TMessageType">The type of the message type.</typeparam>
public class StartMyTvNotification : IMessage<bool> where TContentType : class
{
public StartMyTvNotification(bool isOn)
{
this.Content = isOn;
}
/// <summary>
/// Gets the content of the message.
/// </summary>
public bool Content { get; protected set; }
/// <summary>
/// Gets the content of the message.
/// </summary>
public bool GetContent()
{
return this.Content;
}
/// <summary>
/// Gets the content.
/// </summary>
object IMessage.GetContent()
{
return this.GetContent();
}
}
using System;
/// <summary>
/// Handles chat message subscriptions
/// </summary>
internal class Notification<TMessage> : INotification<TMessage> where TMessage : class, IMessage
{
/// <summary>
/// The callbacks invoked when the handler processes the messages.
/// </summary>
private Action<TMessage, ISubscription> callback;
/// <summary>
/// The conditions that must be met in order to fire the callbacks.
/// </summary>
private Func<TMessage, bool> condition;
/// <summary>
/// Occurs when the subscription is being unsubscribed.
/// </summary>
public event Action<NotificationArgs> Unsubscribing;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ISubscription" /> is active.
/// </summary>
public bool IsActive { get; protected set; }
/// <summary>
/// Registers a callback for when a chat message is published by the MessageCenter
/// </summary>
/// <param name="processor">The message.</param>
/// <returns></returns>
public void Register(
Action<TMessage, ISubscription> processor,
Func<TMessage, bool> condition)
{
this.callback = processor;
this.condition = condition;
this.IsActive = true;
}
/// <summary>
/// Unsubscribes the handler from notifications. This cleans up all of the callback references and conditions.
/// </summary>
public void Unsubscribe()
{
this.callback = null;
this.condition = null;
try
{
this.OnUnsubscribing();
}
finally
{
this.IsActive = false;
}
}
/// <summary>
/// Processes the message by verifying the callbacks can be invoked, then invoking them.
/// </summary>
/// <param name="message">The message.</param>
public void ProcessMessage(TMessage message)
{
if (this.condition != null && !this.condition(message))
{
this.callback(message, this);
return;
}
this.callback(message, this);
}
/// <summary>
/// Called when the notification is being unsubscribed from.
/// </summary>
protected virtual void OnUnsubscribing()
{
var handler = this.Unsubscribing;
if (handler == null)
{
return;
}
handler(new NotificationArgs(this, typeof(TMessage)));
}
}
public class NotificationArgs
{
public NotificationArgs(ISubscription subscription, Type messageType)
{
this.Subscription = subscription;
this.MessageType = messageType;
}
public ISubscription Subscription { get; private set; }
public Type MessageType { get; private set; }
}
這是我用來替換Notifier.OnNotify();
的代碼Notifier.OnNotify();
線:
var timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Interval =
new DateTime(2015, 5, 22, 15, 6, 0)
.Subtract(DateTime.Now)
.TotalMilliseconds;
System.Timers.ElapsedEventHandler handler = null;
handler = (s, e) =>
{
Notifier.OnNotify();
timer.Elapsed -= handler;
timer.Stop();
timer.Dispose();
};
timer.Elapsed += handler;
timer.Start();
顯然,您需要更改日期以適合您希望事件觸發的時間。
我提供了一個相當完整的示例,說明如何觸發事件然后進行清理 。 您應該始終進行這種清理。
現在,這將在后台線程上觸發事件。 如果需要在UI線程上觸發事件,則需要進行適當的調用才能使事件在那里。
我想發表的唯一其他評論是,如果您有任何多線程,則您對OnNotify
的實現會有一個小錯誤。 應該是這樣的:
public void OnNotify()
{
var notify = this.Notify;
if (notify != null)
{
notify();
}
}
這就停止了this.Notify
中的委托的問題被if
和調用之間的另一個線程刪除。 好的習慣是養成以這種方式編寫代碼的習慣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.